7

假设我想发出

public event PropertyChangedHandler PropertyNameChanged;

我该怎么做?我是否需要像定义属性一样定义支持字段?我找不到一个如何使用 EventBuilder 以及如何实际引发事件的示例?

4

2 回答 2

7

我知道这是个老问题,这个答案可能永远不会被接受,但我从谷歌来到这里,所以其他人可能会来到这里。对于那些人,这是一个正确的答案:要创建和事件,您必须:

  1. 创建字段{EventName}
  2. 创建同名事件{EventName}
  3. 创建添加和删除访问器(方法)add_{EventName}remove_{EventName}
  4. 使用任意名称创建 raise 方法(或不创建,因为此步骤是可选的)

德米特里的回答涵盖了第二个和第四个,但这里有一个完整的代码。当然,您必须更改EventNameEventType为您的事件正确的值。

  1. 活动场地

var field = typeBuilder.DefineField("{EventName}", typeof({EventType}), FieldAttributes.Private);

  1. 事件

var eventInfo = typeBuilder.DefineEvent("{EventName}", EventAttributes.None, typeof({EventType}));

  1. 添加访问器和删除访问器;它们非常相似。

    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);

  2. 举法。不是必需的,但是如果您没有方法来引发它,那么创建事件真的没有意义。除非您正在做一些带有反射或调用事件委托值的恶作剧。不管怎样,这里是这个方法的代码。当然,您必须首先获得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 中尽可能少做,以利于您自己和您的理智)。

于 2016-10-18T19:40:04.640 回答
1
  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();
于 2012-11-29T05:57:37.793 回答