尝试将System.Runtime.Remoting.Proxies.RealProxy与Unity结合使用时会引起问题

时间:2020-07-08 16:52:31

标签: c# unity3d

我正在尝试将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似乎已被正确例示,但我无法访问我的公共成员。

1 个答案:

答案 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);
    }
}