假设我想发出
public event PropertyChangedHandler PropertyNameChanged;
我该怎么做?我是否需要像定义属性一样定义支持字段?我找不到一个如何使用 EventBuilder 以及如何实际引发事件的示例?
我知道这是个老问题,这个答案可能永远不会被接受,但我从谷歌来到这里,所以其他人可能会来到这里。对于那些人,这是一个正确的答案:要创建和事件,您必须:
{EventName}
{EventName}
add_{EventName}
和remove_{EventName}
德米特里的回答涵盖了第二个和第四个,但这里有一个完整的代码。当然,您必须更改EventName
并EventType
为您的事件正确的值。
var field = typeBuilder.DefineField("{EventName}", typeof({EventType}), FieldAttributes.Private);
var eventInfo = typeBuilder.DefineEvent("{EventName}", EventAttributes.None, typeof({EventType}));
添加访问器和删除访问器;它们非常相似。
var addMethod = typeBuilder.DefineMethod("add_{EventName}",
MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.SpecialName | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot,
CallingConventions.Standard | CallingConventions.HasThis,
typeof(void),
new[] { typeof({EventType}) });
var generator = addMethod.GetILGenerator();
var combine = typeof(Delegate).GetMethod("Combine", new[] { typeof(Delegate), typeof(Delegate) });
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, field);
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Call, combine);
generator.Emit(OpCodes.Castclass, typeof({EventType}));
generator.Emit(OpCodes.Stfld, field);
generator.Emit(OpCodes.Ret);
eventInfo.SetAddOnMethod(addMethod);
var removeMethod = typeBuilder.DefineMethod("remove_{EventName}",
MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.SpecialName | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot,
CallingConventions.Standard | CallingConventions.HasThis,
typeof(void),
new[] { typeof({EventType}) });
var remove = typeof(Delegate).GetMethod("Remove", new[] { typeof(Delegate), typeof(Delegate) });
var generator = removeMethod.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, field);
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Call, remove);
generator.Emit(OpCodes.Castclass, typeof(PropertyChangedEventHandler));
generator.Emit(OpCodes.Stfld, field);
generator.Emit(OpCodes.Ret);
eventInfo.SetRemoveOnMethod(removeMethod);
举法。不是必需的,但是如果您没有方法来引发它,那么创建事件真的没有意义。除非您正在做一些带有反射或调用事件委托值的恶作剧。不管怎样,这里是这个方法的代码。当然,您必须首先获得XXXEventArgs
在事件内部使用的某种类型的正确构造函数。这个 raise 方法的名称也可能不同,但遵循一些模式通常是更好的主意(如下所示):
var methodBuilder = typeBuilder.DefineMethod("On{EventName}",
MethodAttributes.Family | MethodAttributes.Virtual | MethodAttributes.HideBySig |
MethodAttributes.NewSlot, typeof(void),
new[] { typeof(string) });
var generator = methodBuilder.GetILGenerator();
var returnLabel = generator.DefineLabel();
var eventArgsCtor = typeof({XXXEventArgs}).GetConstructor(new[] { typeof(string) });
generator.DeclareLocal(typeof({EventType}));
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, field);
generator.Emit(OpCodes.Stloc_0);
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Brfalse, returnLabel);
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Newobj, eventArgsCtor);
generator.Emit(OpCodes.Callvirt, typeof(PropertyChangedEventHandler).GetMethod("Invoke"));
generator.MarkLabel(returnLabel);
generator.Emit(OpCodes.Ret);
eventInfo.SetRaiseMethod(methodBuilder);
return methodBuilder;
此外,此方法不是虚拟的或受保护的(MethodAttributes.Family
),但最好将此方法设为非公共和可覆盖的。参数的类型也必须不同并且与XXXEventArgs
类型构造函数兼容。您也可以在此方法中使用更多参数,但我建议不要这样做,因为它会使 IL 更加复杂(您应该在 IL 中尽可能少做,以利于您自己和您的理智)。
TypeBuilder myClass =
myModule.DefineType("MyClass", TypeAttributes.Public);
MethodBuilder onPropertyNameChanged= myClass.DefineMethod("OnPropertyNameChanged",
MethodAttributes.Public, typeof(void), new Type[]{typeof(Object)});
ILGenerator onPropertyNameChangedIl= onPropertyNameChanged.GetILGenerator();
onPropertyNameChangedIl.Emit(OpCodes.Ret);
// Create the event.
EventBuilder propertyNameChanged= myClass.DefineEvent("PropertyNameChanged", EventAttributes.None,
typeof(PropertyChangedHandler)); //should be declared before
propertyNameChanged.SetRaiseMethod(onPropertyNameChanged);
myClass.CreateType();