我正在尝试将RealProxy与Unity3D一起使用,但出现此错误:
NullReferenceException: Object reference not set to an instance of an object
(wrapper ldfld) System.Object.__ldfld_wrapper_000001804BA82020_System.Single(object,intptr,intptr,intptr)
Player.PlayerController.Setup (UMVC.Core.MVC.Interfaces.IBaseView`1[TModel] view) (at Assets/Player/PlayerController.cs:27)
UMVC.Core.MVC.BaseView`2[TModel,TController].Awake () (at <51bf8ffda04c457aa9f14160e1112cbf>:0)
当我尝试让PlayerModel
之类的playerModelInstance.jumpPower
的公共成员时抛出此错误。
PlayerController.cs
后面的代码:
namespace Player
{
public class PlayerController : BaseController<PlayerModel>
{
public override void Setup(IBaseView<PlayerModel> view)
{
base.Setup(view) // you can see the implementation below
// in theory the `Model` is fully initialized after base.Setup(view)
Debug.Log(Model.jumpPower); // THE CODE IS CRASHING HERE
}
} //end PlayerController
}
PlayerModel
:
using System;
using UMVC.Core.MVC;
using UnityEngine;
namespace Player
{
[Serializable]
public class PlayerModel : BaseModel
{
public float jumpPower = 12f;
public override void Initialize()
{
base.Initialize();
isOnFieldDidUpdateEnabled = false;
isOnFieldWillUpdateEnabled = false;
}
}
}
ModelProxy
:您可以在此处查看实现:UMVC.Core/MVC/ModelProxy.cs
BaseModel
:
继承自MarshalByRefObject
的,以使RealProxy正常运行。
BaseController :
using UMVC.Core.MVC.Interfaces;
namespace UMVC.Core.MVC
{
public abstract class BaseController<TModel> where TModel : BaseModel
{
protected TModel Model { get; set; }
protected IBaseView<TModel> View;
protected ModelProxy<TModel> ModelProxy;
public virtual void Setup(IBaseView<TModel> view)
{
View = view;
ModelProxy<TModel> modelProxy = ModelProxy<TModel>.Bind(View.GetModel());
Model = modelProxy.GetTransparentProxy();
Model.Initialize();
ModelProxy = modelProxy;
SubscribeEvents();
}
public virtual void LateSetup()
{
}
protected virtual void SubscribeEvents()
{
ModelProxy.OnFieldWillUpdate += View.OnFieldWillUpdate;
ModelProxy.OnFieldDidUpdate += View.OnFieldDidUpdate;
}
}
}
namespace UMVC.Core.MVC
{
[Serializable]
// MarshalByRefObject are necessary in order to RealProxy be able to work
public abstract class BaseModel : MarshalByRefObject, IBaseModel
{
public bool isOnFieldWillUpdateEnabled = true;
public bool isOnFieldDidUpdateEnabled = true;
public virtual void Initialize()
{
}
}
}
注意:由于单元测试涵盖了此代码(可从UMVC.Core/Tests/MVCTests.cs看到),因此ModelProxy在Unity项目之外工作。
编辑1:
将Debug.Log(Model.jumpPower);
更改为Debug.Log(Model);
的输出
Player.PlayerModel
UnityEngine.Debug:Log(Object)
Player.PlayerController:Setup(IBaseView`1) (at Assets/Player/PlayerController.cs:27)
UMVC.Core.MVC.BaseView`2:Awake()
所以Model
似乎已被正确例示,但我无法访问我的公共成员。
答案 0 :(得分:1)
作为RealProxy
的替代品,我使用了类似于Microsoft INotifyPropertyChanged
的模式:
public interface INotifyPropertyChanged
{
event Delegates.OnFieldWillUpdate OnFieldWillUpdate;
event Delegates.OnFieldDidUpdate OnFieldDidUpdate;
}
将我的BaseModel
修改如下:
[Serializable]
public abstract class BaseModel: IBaseModel, INotifyPropertyChanged {
public bool isOnFieldWillUpdateEnabled = true;
public bool isOnFieldDidUpdateEnabled = true;
public virtual event Delegates.OnFieldWillUpdate OnFieldWillUpdate;
public virtual event Delegates.OnFieldDidUpdate OnFieldDidUpdate;
public virtual void Initialize() {}
protected virtual void RaiseOnFieldWillUpdate<T>(T newValue, [CallerMemberName] string propertyName = null) {
var eventArgs = new PropertyChangedEventArgs(propertyName);
object oldValue;
var type = GetType();
var field = type.GetField(eventArgs.PropertyName);
if (field != null) {
oldValue = field.GetValue(this);
}
else {
oldValue = type.GetProperty(eventArgs.PropertyName) ? .GetValue(this);
}
OnFieldWillUpdate?.Invoke(this, newValue, oldValue, eventArgs);
}
protected virtual void RaiseOnFieldDidUpdate<T>(T newValue, [CallerMemberName] string propertyName = null) {
OnFieldDidUpdate?.Invoke(this, newValue, new PropertyChangedEventArgs(propertyName));
}
protected virtual bool Set<T>(ref T field, T value, Expression<Func<T>> property) {
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
string propertyName = ((MemberExpression) property.Body).Member.Name;
if (isOnFieldWillUpdateEnabled) RaiseOnFieldWillUpdate(value, propertyName);
field = value;
if (isOnFieldDidUpdateEnabled) RaiseOnFieldDidUpdate(field, propertyName);
return true;
}
}
像这样使用它:
[Serializable]
public class MvcModel: BaseModel
{
[SerializeField] private int val;
public int Value {
get => val;
set => Set(ref val, value, () => Value);
}
}