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

C# 與 .NET 事件注入模式:從控制反轉到靈活的流程擴展(非 AOP)

     編輯:關於C#

所謂「事件注入」是借鑑依賴注入(DI)思想臨時提出的概念。DI 讓客戶類僅依賴服務介面,由執行期上下文或容器實例化具體服務並注入,實現解耦。事件注入則將這一思想應用於**流程擴展點**:在核心業務流程的關鍵節點(如前置/後置)定義事件,由外部組件透過反射動態訂閱並注入行為,實現**開放封閉原則(OCP)**下的流程無侵入式擴展。

核心場景:遊戲攻擊流程擴展

假設基礎攻擊邏輯已穩定,後續需求頻變:新增「技能前搖」、「擊殺獲得金幣」等橫切關注點。若直接修改核心 Attack 方法違反 OCP。事件注入提供優雅方案:

1. 定義事件與事件參數

// 攻擊前事件(無額外數據)
public static event EventHandler<EventArgs> BeforeAttackEvent;
protected virtual void BeforeAttack(EventArgs e) {
    BeforeAttackEvent?.Invoke(this, e);
}

// 攻擊後事件(攜帶金幣數據)
public static event EventHandler<GameEventArgs> AttackedEvent;
protected virtual void OnAttacked(GameEventArgs e) {
    AttackedEvent?.Invoke(this, e);
}

// 自訂事件參數
public class GameEventArgs : EventArgs {
    public int Coin { get; set; }
    public GameEventArgs(int coin = 0) { Coin = coin; }
}

事件定義處**僅宣告委派**,不實作具體邏輯,確保核心碼純淨。

2. 在核心流程植入注入點

public void Attack(Monster monster) {
    BeforeAttack(EventArgs.Empty);  // 注入點 1:攻擊前

    // ... 原有攻擊邏輯(武器判斷、傷害計算、死亡判定)...

    var e = new GameEventArgs();    // 注入點 2:攻擊後
    OnAttacked(e);
}

3. 實現具體擴展行為(外部組件)

[Extension("遊戲規則_攻擊前", "1.0.0.0", "作者")]
public class GameRule_BeforeAttack {
    public GameRule_BeforeAttack() {
        Role.BeforeAttackEvent += BeforeAttack;
    }
    void BeforeAttack(object sender, EventArgs e) {
        Console.WriteLine("技能前搖 扭動身體...");
    }
}

[Extension("遊戲規則_攻擊後", "1.0.0.0", "作者")]
public class GameRule_AfterAttack {
    private readonly Random _rnd = new Random();
    public GameRule_AfterAttack() {
        Role.AttackedEvent += Attacked;
    }
    void Attacked(object sender, GameEventArgs e) {
        var role = sender as Role;
        int coin = _rnd.Next(1, 10);
        role.Coin += coin;
        Console.WriteLine($"獲得 {coin} 金幣,總計 {role.Coin}");
    }
}

擴展類別透過建構子自動訂閱靜態事件,**與核心程式碼零耦合**,可獨立編譯部署為 DLL。

4. 自訂屬性與反射自動注入引擎

[AttributeUsage(AttributeTargets.Class)]
public class ExtensionAttribute : Attribute {
    public string Description { get; }
    public string Version { get; }
    public string Author { get; }
    public ExtensionAttribute(string desc, string ver, string auth) {
        Description = desc; Version = ver; Author = auth;
    }
}

// 啟動時掃描目錄下所有 DLL,實例化標記了 ExtensionAttribute 的類別
var dir = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory);
foreach (var file in dir.GetFiles("*.dll")) {
    var asm = Assembly.LoadFrom(file.FullName);
    foreach (var type in asm.GetTypes()) {
        var attrs = type.GetCustomAttributes(typeof(ExtensionAttribute), false);
        foreach (var attr in attrs) {
            asm.CreateInstance(type.FullName);  // 觸發建構子完成訂閱
        }
    }
}

優勢與應用

  • 零侵入:核心流程不變,新增/修改規則只需新增/替換 DLL。
  • 多實例共存:同一事件可訂閱多個處理器,按載入順序執行。
  • 可視化管理:可擴展管理類增加啟用/停用、過期時間、版本控制等元數據。
  • 非 AOP:無需織入器、代理或 IL 重寫,純 .NET 事件與反射實現,輕量透明。

效果驗證

啟動程式,攻擊怪物,控制台輸出:

技能前搖 扭動身體...
攻擊成功!怪物哥布林損失 20 HP
本次攻擊獲得了... 7 個金幣, 當前金幣為 7 個

若新增「技能後擺」規則 DLL 並重啟,輸出自動變為:

技能前搖 扭動身體...
攻擊成功!怪物哥布林損失 20 HP
技能後擺 O(∩_∩)O 哈哈哈~...
本次攻擊獲得了... 3 個金幣, 當前金幣為 10 個

核心代碼**完全未修改**,完美實現 OCP 原則下的流程級擴展。

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