Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/268.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在值更改之前调用castle dynamic proxy PropertyChanged_C#_.net_Proxy_Castle Windsor_Inotifypropertychanged - Fatal编程技术网

C# 在值更改之前调用castle dynamic proxy PropertyChanged

C# 在值更改之前调用castle dynamic proxy PropertyChanged,c#,.net,proxy,castle-windsor,inotifypropertychanged,C#,.net,Proxy,Castle Windsor,Inotifypropertychanged,我用以下代码创建了一个单元测试库,它尝试代理INotifyPropertyChanged,并监听它 using System; using System.ComponentModel; using System.Runtime.CompilerServices; using Castle.Core; using Castle.DynamicProxy; using Castle.MicroKernel.Registration; using Castle.Windsor; using Micro

我用以下代码创建了一个单元测试库,它尝试代理INotifyPropertyChanged,并监听它

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Castle.Core;
using Castle.DynamicProxy;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Component = Castle.MicroKernel.Registration.Component;

namespace UnitTestWinsorContainer {
    [TestClass]
    public class UnitTestDynamicProxy {
        public static IWindsorContainer Container { get; protected set; }

        [ClassInitialize]
        public static void TestInit(TestContext tc) {
            Container = new WindsorContainer();
            Container.Register(Classes.FromThisAssembly().BasedOn<IInterceptor>());
            Container.Register(Component.For(typeof(DummyViewModel))
                .Interceptors(InterceptorReference.ForType<DummyInterceptor>()).Anywhere);
        }

        [TestMethod]
        public void TestResolve() {
            Console.WriteLine("Test Starts");
            var i = Container.Resolve<DummyViewModel>();
            Assert.AreEqual(i.GetType().Name, "DummyViewModelProxy");
            i.PropertyChanged += OnPropertyChanged;
            i.Qty = 100;
            Console.WriteLine($"Get Qty after PropertyChanged of Proxy Qty=[{i.Qty}]");

            var j = new DummyViewModel();
            j.PropertyChanged += OnPropertyChanged;
            j.Qty = 100;
        }

        private static void OnPropertyChanged(object sender, PropertyChangedEventArgs e) {
            var d = sender as DummyViewModel;
            if (d != null) Console.WriteLine($"PropertyName=[{e.PropertyName}] Qty=[{d.Qty}]");
        }
    }

    public class DummyInterceptor : IInterceptor {
        public void Intercept(IInvocation invocation) {
            invocation.Proceed();
        }
    }

    public class DummyViewModel : Bindable {
        private int qty;

        /// <summary>
        ///     My Property
        /// </summary>
        public int Qty {
            get { return qty; }
            set { SetProperty(ref qty, value); }
        }
    }

    public class Bindable : INotifyPropertyChanged {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null) {
            if (Equals(storage, value)) return false;

            storage = value;
            RaisePropertyChanged(propertyName);

            return true;
        }

        protected virtual bool SetProperty<T>(ref T storage, T value, Action onChanged,
            [CallerMemberName] string propertyName = null) {
            if (Equals(storage, value)) return false;

            storage = value;
            onChanged?.Invoke();
            RaisePropertyChanged(propertyName);

            return true;
        }

        protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) {
            OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }

        protected virtual void OnPropertyChanged(PropertyChangedEventArgs args) {
            PropertyChanged?.Invoke(this, args);
        }
    }
}
这意味着在基础值未更改时调用代理的第一个属性更改事件


为什么呢?我做错什么了吗?

问题是Castle如何拦截带有
ref
参数的方法。我相信下面生成的代理类类似于:

public class DummyViewModelProxy : DummyViewModel
{
    protected override bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        var interceptor = new DummyInterceptor();
        IInvocation invocation = new Invocation(storage, value, propertyName);
        interceptor.Intercept(invocation);
        storage = (T)invocation.Arguments[0];
        return (bool)invocation.ReturnValue;
    }

    ....
}
公共类DummyViewModelProxy:DummyViewModel
{
受保护的重写bool SetProperty(ref T storage,T value,[CallerMemberName]string propertyName=null)
{
var interceptor=新的DummyInterceptor();
IInvocation调用=新调用(存储、值、属性名称);
拦截(调用);
storage=(T)invocation.Arguments[0];
return(bool)invocation.ReturnValue;
}
....
}

然后,
ref
参数链断开,在调用
PropertyChanged
后,值被真正设置。

我发现类
Bindable
中的两个
SetProperty
函数上的“虚拟”修饰符导致了结果。但我仍然想知道为什么,如果有人知道,请留下一个答案,谢谢。标记
SetProperty
virtual意味着Castle DynamicProxy将代理这些成员,那么DynamicProxy可能会导致
CallerMemberName
被填充的问题?@JonathonRossi不,不会。我将更新我的代码。同时显示结果。我感觉
ref
是问题所在!非常感谢你的证明!我们最近添加了一些关于按参考参数的文档:
public class DummyViewModelProxy : DummyViewModel
{
    protected override bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        var interceptor = new DummyInterceptor();
        IInvocation invocation = new Invocation(storage, value, propertyName);
        interceptor.Intercept(invocation);
        storage = (T)invocation.Arguments[0];
        return (bool)invocation.ReturnValue;
    }

    ....
}