在 Reflection.Emit 中為動態類型添加事件,比屬性/方法複雜得多:必須手動實現 add_EventName / remove_EventName 兩個存取器,且底層依賴一個同名私有委托字段。本文拆解完整流程,含 Delegate.Combine/Remove 的正確用法與 EventBuilder 綁定細節。
public event EventHandler<RandGeneratedEventArgs> RandGenerated;
protected virtual void OnRandGenerated(int rand)
{
RaiseRandGeneratedEvent(rand);
}
private void RaiseRandGeneratedEvent(int rand)
{
var handler = RandGenerated; // 線程安全拷貝
if (handler != null)
{
handler(this, new RandGeneratedEventArgs(rand));
}
}
Reflector 反編譯顯示:編譯器會生成 private EventHandler<T> RandGenerated; 字段,add_RandGenerated/remove_RandGenerated 分別調用 Delegate.Combine/Remove 修改該字段。
var eventType = typeof(EventHandler<RandGeneratedEventArgs>);
var backingField = typeBuilder.DefineField(
"RandGenerated", eventType, FieldAttributes.Private);
var evtBuilder = typeBuilder.DefineEvent(
"RandGenerated", EventAttributes.None, eventType);
var addMethod = typeBuilder.DefineMethod(
"add_RandGenerated",
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Virtual,
null,
new[] { eventType });
var ilAdd = addMethod.GetILGenerator();
// this.RandGenerated = Delegate.Combine(this.RandGenerated, value)
ilAdd.Emit(OpCodes.Ldarg_0); // this
ilAdd.Emit(OpCodes.Ldarg_0); // this
ilAdd.Emit(OpCodes.Ldfld, backingField); // this.RandGenerated
ilAdd.Emit(OpCodes.Ldarg_1); // value (新委托)
ilAdd.Emit(OpCodes.Call, typeof(Delegate).GetMethod("Combine", new[] { eventType, eventType }));
ilAdd.Emit(OpCodes.Castclass, eventType); // 轉換回強類型
ilAdd.Emit(OpCodes.Stfld, backingField); // 存回字段
ilAdd.Emit(OpCodes.Ret);
evtBuilder.SetAddOnMethod(addMethod);
var removeMethod = typeBuilder.DefineMethod(
"remove_RandGenerated",
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Virtual,
null,
new[] { eventType });
var ilRemove = removeMethod.GetILGenerator();
// this.RandGenerated = Delegate.Remove(this.RandGenerated, value)
ilRemove.Emit(OpCodes.Ldarg_0);
ilRemove.Emit(OpCodes.Ldarg_0);
ilRemove.Emit(OpCodes.Ldfld, backingField);
ilRemove.Emit(OpCodes.Ldarg_1);
ilRemove.Emit(OpCodes.Call, typeof(Delegate).GetMethod("Remove", new[] { eventType, eventType }));
ilRemove.Emit(OpCodes.Castclass, eventType);
ilRemove.Emit(OpCodes.Stfld, backingField);
ilRemove.Emit(OpCodes.Ret);
evtBuilder.SetRemoveOnMethod(removeMethod);
var raiseMethod = typeBuilder.DefineMethod(
"RaiseRandGeneratedEvent",
MethodAttributes.Private | MethodAttributes.HideBySig,
null,
new[] { typeof(int) });
var ilRaise = raiseMethod.GetILGenerator();
var labelIfNull = ilRaise.DefineLabel();
var labelEnd = ilRaise.DefineLabel();
// var temp = this.RandGenerated
ilRaise.Emit(OpCodes.Ldarg_0);
ilRaise.Emit(OpCodes.Ldfld, backingField);
ilRaise.Emit(OpCodes.Dup); // 複製一份用於後續判空
ilRaise.Emit(OpCodes.Brtrue_S, labelIfNull);
ilRaise.Emit(OpCodes.Pop); // 為 null 彈出
ilRaise.Emit(OpCodes.Br_S, labelEnd);
ilRaise.MarkLabel(labelIfNull);
// temp(this, new RandGeneratedEventArgs(rand))
ilRaise.Emit(OpCodes.Ldarg_0); // this
ilRaise.Emit(OpCodes.Ldarg_1); // rand
ilRaise.Emit(OpCodes.Newobj, typeof(RandGeneratedEventArgs).GetConstructor(new[] { typeof(int) }));
ilRaise.Emit(OpCodes.Callvirt, eventType.GetMethod("Invoke"));
ilRaise.MarkLabel(labelEnd);
ilRaise.Emit(OpCodes.Ret);
ildasm 或 Reflector 檢查 .event 元數據是否包含 .addon/.removeon。+=/-= 編譯為 call add_RandGenerated/call remove_RandGenerated。Raise 方法的拷貝模式 (var temp = field) 保證線程安全。事件發射是 Emit 進階能力的試金石,搞懂委托合併/移除與字段交互,才能構建完整的動態領域模型。