使用 Reflection.Emit 实现 INotifyPropertyChanged
使用 C#/.Net 4.0,我将数据存储在 BindingList 中,其中 dataRow 在运行时通过 Reflection.Emit 定义。 (传入数据的结构各不相同,并由外部源定义。) 在我第一次涉足反射和 IL 世界时遇到了一些困难之后,我已经能够创建我的 dataRow,用值填充它,填充我的 BindingList 并在网格中显示结果。 我现在尝试在对数据进行更改时实现 INotifyPropertyChanged 接口和 PropertyChangedEventHandler 。使用此作为指导,我有运行的代码,但 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);
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果您尝试修改程序集的 IL,我会避免反射。您可能会遇到程序集锁定问题。
至于解决方案,请尝试这些链接。
自定义编织
使用 postsharp
If you are trying to modify the IL of an assembly I would stear clear of reflection. You may run into assembly locking issues.
As for a solution try these links.
Custom weaving
Using postsharp