使用Reflection.Emit实现INotifyPropertyChanged

5
使用C#/.Net 4.0,我正在使用Reflection.Emit在运行时定义dataRow来存储数据。 (传入数据的结构因外部源而异。) 在第一次尝试反射和IL的世界后有些困难,但我已经能够创建我的dataRow并填充它的值,填充我的BindingList并在网格中显示结果。 现在我正在尝试实现INotifyPropertyChanged接口和PropertyChangedEventHandler,当数据发生更改时使用它们。 使用this作为指南,我编写了代码,但似乎RaisePropertyChanged事件没有触发或者它只是没有做任何事情。 当我通过ildasm.exe将我的动态版本与正常/静态版本进行比较时,我看到remove_PropertyChanged和add_PropertyChanged方法存在重大差异。 是否有人可以提供一些关于如何通过反射实现INotifyPropertyChanged接口的提示或示例?
经过进一步检查,似乎事件字段必须为空,以便不调用PropertyChangedEventHandler。我在RaiseProprtyChanged方法生成器中添加了一些消息框,并发现等价于if(PropertyChanged != null)的内容返回零/假,因此什么也没有发生。如果我将OpCodes.Brtrue更改为OpCodes.Brfalse,我会收到“对象引用未设置为对象实例”的消息。 感觉好像我错过了一些简单的东西,但我找不到它。
            //implement IINotifyPropertyChanged interface
        tb.AddInterfaceImplementation(typeof(INotifyPropertyChanged));

        //property changed event handler
        FieldBuilder eventField = tb.DefineField("PropertyChanged", typeof(PropertyChangedEventHandler), FieldAttributes.Private);

        EventBuilder eb = tb.DefineEvent("PropertyChanged", EventAttributes.None, typeof(PropertyChangedEventHandler));

        MethodBuilder mbEV = tb.DefineMethod("remove_PropertyChanged", MethodAttributes.Public |
            MethodAttributes.SpecialName | MethodAttributes.NewSlot |
            MethodAttributes.HideBySig | MethodAttributes.Virtual |
            MethodAttributes.Final, null, new[] { typeof(PropertyChangedEventHandler) });

        MethodImplAttributes eventMethodFlags = MethodImplAttributes.Managed; //| MethodImplAttributes.Synchronized;
        mbEV.SetImplementationFlags(eventMethodFlags);
        il = mbEV.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldfld, eventField);
        il.Emit(OpCodes.Ldarg_1);
        il.EmitCall(OpCodes.Call, typeof(Delegate).GetMethod("Remove", new[] { typeof(Delegate), typeof(Delegate) }), null);
        il.Emit(OpCodes.Castclass, typeof(PropertyChangedEventHandler));
        il.Emit(OpCodes.Stfld, eventField);
        il.Emit(OpCodes.Ret);
        MethodInfo miRemoveEvent = typeof(INotifyPropertyChanged).GetMethod("remove_PropertyChanged");
        tb.DefineMethodOverride(mbEV, miRemoveEvent);
        eb.SetRemoveOnMethod(mbEV);

        mbEV = tb.DefineMethod("add_PropertyChanged", MethodAttributes.Public |
            MethodAttributes.SpecialName | MethodAttributes.NewSlot |
            MethodAttributes.HideBySig | MethodAttributes.Virtual |
            MethodAttributes.Final, null, new[] { typeof(PropertyChangedEventHandler) });

        mbEV.SetImplementationFlags(eventMethodFlags);
        il = mbEV.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldfld, eventField);
        il.Emit(OpCodes.Ldarg_1);
        il.EmitCall(OpCodes.Call, typeof(Delegate).GetMethod("Combine", new[] { typeof(Delegate), typeof(Delegate) }), null);
        il.Emit(OpCodes.Castclass, typeof(PropertyChangedEventHandler));
        il.Emit(OpCodes.Stfld, eventField);
        il.Emit(OpCodes.Ret);
        MethodInfo miAddEvent = typeof(INotifyPropertyChanged).GetMethod("add_PropertyChanged");
        tb.DefineMethodOverride(mbEV, miAddEvent);
        eb.SetAddOnMethod(mbEV);

        MethodInfo msgboxMethodInfo = typeof(System.Windows.Forms.MessageBox).GetMethod("Show", BindingFlags.Public | BindingFlags.Static, null, CallingConventions.Standard, new Type[] { typeof(String) }, null);

        MethodBuilder mbRaisePropertyChanged = tb.DefineMethod("RaisePropertyChanged", MethodAttributes.Virtual, null, new Type[] { typeof(string) });
        il = mbRaisePropertyChanged.GetILGenerator();
        System.Reflection.Emit.Label labelExit = il.DefineLabel();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldfld, eventField);
        il.Emit(OpCodes.Ldnull);
        il.Emit(OpCodes.Ceq); //this is returning false
        il.Emit(OpCodes.Brtrue, labelExit);
        il.Emit(OpCodes.Nop); //I never get here
        il.Emit(OpCodes.Ldstr, "After If");
        il.EmitCall(OpCodes.Call, msgboxMethodInfo, null);
        il.Emit(OpCodes.Pop);
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldfld, eventField);
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldarg_1);
        il.Emit(OpCodes.Newobj, typeof(PropertyChangedEventArgs).GetConstructor(new[] { typeof(string) }));
        il.EmitCall(OpCodes.Callvirt, typeof(PropertyChangedEventHandler).GetMethod("Invoke"), null);
        il.Emit(OpCodes.Nop);
        il.Emit(OpCodes.Nop);
        il.MarkLabel(labelExit);
        il.Emit(OpCodes.Ret);

你可以尝试使用Castle动态代理的另一种解决方案,你可以在http://dynamiccontrollers.codeplex.com/中看到类似解决方案的示例。这不是直接回答你的问题,而是提供了一种替代方法。 - Elisha
请展示一下你目前所拥有的代码。当使用 Emit 生成方法时,与“真实”版本相比有什么区别?显然,解决方案将涉及修复您的版本以匹配自动生成的事件。 - Kirk Woll
我在上面添加了一些代码和额外的信息。 - Dan Schubel
1个回答

3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接