C# 3.0 引入的 Lambda 表達式、LINQ、擴展方法等特性,底層無一不建立在委托與事件機制之上。理解委托的內存模型、多播鏈表、編譯器生成細節,是掌握現代 C# 函數式編程風格的鑰匙。
C 語言函數指針僅存地址,缺乏參數/返回類型檢查。.NET 委托封裝三要素:
// 源碼
public delegate int Calculate(int x, int y);
// 編譯器生成(等價)
[Serializable]
public sealed class Calculate : MulticastDelegate
{
public Calculate(object @object, IntPtr method) { ... }
public virtual int Invoke(int x, int y) { ... }
public virtual IAsyncResult BeginInvoke(int x, int y, AsyncCallback cb, object obj) { ... }
public virtual int EndInvoke(IAsyncResult result) { ... }
}
關鍵點:
MulticastDelegate → 自動擁有調用列表管理能力Invoke 同步調用、BeginInvoke/EndInvoke 異步模式(APM)| 成員 | 類型 | 用途 |
|---|---|---|
| Method | MethodInfo | 獲取目標方法元數據(名稱、參數、返回類型) |
| Target | object | 目標實例(靜態方法為 null) |
| GetInvocationList() | Delegate[] | 獲取調用鏈中每個單播委托,支持逐個調用/異常隔離 |
| Combine / Remove | static Delegate | 多播鏈表添加/移除(+= / -= 底層調用) |
Action a = () => Console.WriteLine("1");
a += () => Console.WriteLine("2");
a += () => throw new Exception("錯誤");
a += () => Console.WriteLine("3");
a();
// 輸出:1, 2, 然後拋出異常,"3" 不會執行
// 返回值:僅保留最後一個委托的返回值
警示:多播委托異常會中斷後續調用,需用 GetInvocationList() 手動遍歷捕獲異常。
public delegate double CalcStrategy(double a, double b);
public class Calculator
{
public CalcStrategy Strategy { get; set; } = (a,b) => a + b;
public double Execute(double a, double b) => Strategy(a, b);
}
// 運行時切換策略
var calc = new Calculator();
calc.Strategy = (a,b) => a - b; // 減法
calc.Strategy = (a,b) => a * b; // 乘法
calc.Strategy = Math.Pow; // 靜態方法也可
委托是 C# 函數一等公民的基石:類型安全、面向對象、支持多播、編譯器自動生成調用基礎設施。下一篇將深入事件模式、編譯器生成的 add/remove 方法、以及如何通過事件實現鬆耦合通知機制,為 Lambda 表達式與 LINQ 鋪路。