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

C# 委派與事件深度實戰:從函式傳遞到觀察者模式的完整演進

     編輯:關於C#

委派與事件是 .NET 類型系統的皇冠上的明珠:委派實現「函式即資料」,事件在委派之上建立「發布-訂閱」鬆耦合協作模型,是觀察者模式的語言級原生支援。

委派:型別安全的函式指標


// 宣告委派型別:定義可引用函式的簽名
public delegate void GreetingDelegate(string name);

// 具體函式
void EnglishGreeting(string n) => Console.WriteLine($"Morning, {n}");
void ChineseGreeting(string n) => Console.WriteLine($"早上好, {n}");

// 使用:將函式作為參數傳遞
void GreetPeople(string name, GreetingDelegate greeter) => greeter(name);

// 呼叫
GreetPeople("Jimmy", EnglishGreeting);  // Morning, Jimmy
GreetPeople("張三", ChineseGreeting);    // 早上好, 張三

核心特性

  • 多播:del += Method 串聯多個函式,呼叫時依序執行
  • 型別安全:簽名不符編譯失敗
  • 實例/靜態/匿名/Lambda 皆可綁定
  • 編譯器產生繼承 MulticastDelegate 的密封類別,含 Invoke/BeginInvoke/EndInvoke

事件:委派的封裝與存取控制

直接暴露委派欄位兩大弊端:

  1. 外部可任意 = 覆寫,清除既有訂閱
  2. 外部可直接 Invoke() 觸發,破壞封裝

event 關鍵字解決:


public class GreetingManager {
    // 僅允許 += / -=,編譯器產生私有委派欄位 + add_/remove_ 存取器
    public event GreetingDelegate MakeGreet;

    public void Greet(string name) => MakeGreet?.Invoke(name); // 安全觸發
}

// 使用
var gm = new GreetingManager();
gm.MakeGreet += EnglishGreeting;  // 訂閱
gm.MakeGreet += ChineseGreeting;  // 多播
gm.MakeGreet -= EnglishGreeting;  // 退訂
gm.Greet("Jimmy");                // 觸發

事件編譯器展開


// 來源
public event GreetingDelegate MakeGreet;

// 編譯器產生
private GreetingDelegate _makeGreet;
[MethodImpl(MethodImplOptions.Synchronized)]
public void add_MakeGreet(GreetingDelegate v) => _makeGreet = Delegate.Combine(_makeGreet, v);
[MethodImpl(MethodImplOptions.Synchronized)]
public void remove_MakeGreet(GreetingDelegate v) => _makeGreet = Delegate.Remove(_makeGreet, v);

觀察者模式:熱水器-警報器-顯示器完整實現

領域模型

  • Subject (被觀察者):Heater,狀態 = 溫度
  • Observer (觀察者):Alarm(聲音)、Display(視覺)
  • 流程:觀察者訂閱 → Subject 狀態變化 → 主動通知所有觀察者

.NET 標準事件模式


// 事件引數:攜帶領域資料
public class BoiledEventArgs : EventArgs {
    public int Temperature { get; }
    public BoiledEventArgs(int t) => Temperature = t;
}

// 被觀察者
public class Heater {
    public event EventHandler Boiled; // 標準雙參數委派

    protected virtual void OnBoiled(BoiledEventArgs e) => Boiled?.Invoke(this, e);

    public void BoilWater() {
        for (int t=0; t<=100; t++) {
            if (t > 95) OnBoiled(new BoiledEventArgs(t));
        }
    }
}

// 觀察者
public class Alarm {
    public void OnBoiled(object sender, BoiledEventArgs e) {
        var h = (Heater)sender;
        Console.WriteLine($"[{h.Model}] 警報:水溫 {e.Temperature}°C");
    }
}
public class Display {
    public static void OnBoiled(object sender, BoiledEventArgs e) {
        Console.WriteLine($"顯示:水溫 {e.Temperature}°C");
    }
}

// 組裝
var heater = new Heater { Model = "RealFire 001" };
var alarm = new Alarm();
heater.Boiled += alarm.OnBoiled;
heater.Boiled += Display.OnBoiled;
heater.BoilWater();

關鍵設計規範

  1. 委派命名:xxxEventHandler
  2. 委派簽名:void (object sender, TEventArgs e),TEventArgs : EventArgs
  3. 事件命名:移除 EventHandler 後綴(BoiledEventHandler → Boiled)
  4. 引發方法:protected virtual void OnXxx(XxxEventArgs e),衍生類可覆寫/擴展
  5. sender 傳遞 this,觀察者可回溯來源存取完整物件

匿名方法與 Lambda 現代寫法


// 匿名方法 (C# 2.0)
heater.Boiled += delegate(object s, BoiledEventArgs e) { ... };

// Lambda (C# 3.0+)
heater.Boiled += (s, e) => Console.WriteLine($"溫度: {e.Temperature}");

// 區域函式 (C# 7.0+)
void LocalHandler(object s, BoiledEventArgs e) => ...;
heater.Boiled += LocalHandler;

委派與事件是 .NET 非同步、UI、領域事件、微服務整合事件的基石。精通其機制與模式,是構建鬆耦合、可擴展、可測試系統的核心能力。

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