【问题标题】:Implementing INotifyPropertyChanged with Reflection.Emit使用 Reflection.Emit 实现 INotifyPropertyChanged
【发布时间】:2011-04-08 01:22:31
【问题描述】:

使用 C#/.Net 4.0,我将数据存储在 BindingList 中,其中 dataRow 在运行时通过 Reflection.Emit 定义。 (传入数据的结构各不相同,由外部来源定义。) 在我第一次涉足反射和 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);

【问题讨论】:

  • 您可以尝试使用城堡动态代理的替代解决方案,您可以在dynamiccontrollers.codeplex.com 中看到类似解决方案的示例。这不是您问题的直接解决方案,只是一种替代方法。
  • 请向我们展示您目前获得的代码。这些方法的“真实”版本与您在 Emit 中自己生成的方法有什么区别?显然,解决方案将涉及修复您的版本以匹配为事件自动生成的内容。
  • 我在上面添加了一些代码和附加信息

标签: c# reflection inotifypropertychanged


【解决方案1】:

如果您尝试修改程序集的 IL,我会避开反射。您可能会遇到程序集锁定问题。

至于解决方案,请尝试这些链接。

定制编织

使用后锐化

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-05
    • 2023-03-29
    • 1970-01-01
    • 2023-03-26
    • 2011-01-19
    • 2010-10-04
    • 1970-01-01
    • 2014-06-02
    相关资源
    最近更新 更多