首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

跨組件組裝事件訂閱:無引用依賴下的動態事件掛載實戰

     編輯:關於.NET

場景:組件 A 定義事件,組件 B **未引用 A** 卻需在運行時動態訂閱該事件。傳統強型別訂閱失效,需動態反射 + DynamicMethod 發 IL 實現「橋接委託」。

核心挑戰

  • B 無法在編譯期引用 A 的事件委託型別(如 FarEventArgs)
  • 事件簽名為 (object sender, FarEventArgs e),B 僅持有 EventHandler (sender, EventArgs)
  • 需動態生成符合目標事件簽名的委託實例

動態掛載代碼全解析


private void btnAttachEvent_Click(object sender, EventArgs e) {
    // 1. 取得事件資訊
    Type formType = fireEventForm.GetType();
    EventInfo evtInfo = formType.GetEvent("SomeEvent");

    // 2. 準備目標處理器(簽名:object, EventArgs)
    EventHandler tempHandler = new EventHandler(this.farEventHandler);
    MethodInfo targetMethod = tempHandler.Method;

    // 3. 發射 DynamicMethod:橋接簽名差異
    // 目標簽名:void (object, FarEventArgs)
    var dm = new DynamicMethod(
        "BridgeMethodForAttachEvent",
        typeof(void),
        new Type[] { typeof(object), assembly.GetType("FireFarEvent.FarEventArgs") },
        this.GetType()); // 關聯模組以訪問私有成員

    var il = dm.GetILGenerator();
    // IL 邏輯:this.farEventHandler(sender, (EventArgs)e)
    il.Emit(OpCodes.Nop);
    il.Emit(OpCodes.Ldarg_0);      // this
    il.Emit(OpCodes.Ldarg_2);      // FarEventArgs e
    il.Emit(OpCodes.Ldarg_1);      // object sender
    il.Emit(OpCodes.Call, targetMethod);
    il.Emit(OpCodes.Nop);
    il.Emit(OpCodes.Ret);

    dm.DefineParameter(1, ParameterAttributes.In, "sender");
    dm.DefineParameter(2, ParameterAttributes.In, "e");

    // 4. 創建符合事件簽名的委託並掛載
    Delegate bridgeDelegate = dm.CreateDelegate(evtInfo.EventHandlerType);
    evtInfo.AddEventHandler(fireEventForm, bridgeDelegate);
}

關鍵技術點解析

  1. 參數順序調整: 事件簽名通常為 (sender, e),但處理器習慣 (this, e, sender),IL 中 Ldarg_2, Ldarg_1 完成順序交換。
  2. 型別轉換隱含: FarEventArgs 繼承 EventArgs,傳遞時隱含向上轉型,CLR 自動驗證。
  3. DynamicMethod 權限: 建構時傳入 this.GetType() 作為 m 參數,賦予動態方法訪問當前類型私有/內部成員的權限。
  4. 委託創建: CreateDelegate(evtInfo.EventHandlerType) 確保生成委託與事件精確匹配。

動態卸載


private void btnDetachEvent_Click(object sender, EventArgs e) {
    if (assembly == null || fireEventForm == null || farEventEventInfo == null) return;
    farEventEventInfo.RemoveEventHandler(fireEventForm, farEventDelegate);
}

保留 farEventDelegate 引用以便精確移除。

適用場景與注意事項

  • 插件架構:宿主與插件鬆耦合,僅共享契約程式集
  • 熱插拔:運行時動態載入/卸載組件並連接事件
  • 效能: IL 發射有開銷,頻繁掛載/卸載場景建議快取 DynamicMethod 結果
  • 安全: 需 ReflectionPermission 與 SecurityPermission (UnmanagedCode/ControlEvidence)

總結

透過 DynamicMethod 發射橋接 IL,打破編譯期型別依賴,實現真正的「跨組件、無引用、動態事件訂閱」。這是插件系統、擴展點架構的核心基礎設施技術。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved