委派 是 C# 實現「函數作為一等公民」的基石,事件 建立在委派之上,Lambda 表達式則是匿名方法的語法糖,三者環環相扣。本文將從委派本質、多播機制、事件模式、協變逆變、匿名方法到 Lambda 及運算式樹,完整串聯核心知識脈絡。
delegate int Transformer(int x); // 宣告委派型別
static int Square(int x) => x * x;
static int Cube(int x) => x * x * x;
Transformer t = Square; // 指派靜態方法
Transformer t2 = new Transformer(Cube); // 等價寫法
int result = t(5); // 呼叫委派 (25)
MulticastDelegate。Transformer multi = Square;
multi += Cube; // 串聯
multi += x => -x; // 加入 Lambda
foreach (Transformer d in multi.GetInvocationList())
Console.WriteLine(d(3)); // 9, 27, -3
int last = multi(3); // 只取最後一個回傳值 (-3)
每個委派實例維護一個調用清單,+=/-= 建立新委派實例 (不可變)。
// 1. 宣告事件 (慣用 EventHandler 模式)
public class Timer
{
public event EventHandler Elapsed;
protected virtual void OnElapsed(ElapsedEventArgs e)
{
Elapsed?.Invoke(this, e); // Null 條件運算子,執行緒安全
}
}
// 2. 訂閱
var timer = new Timer();
timer.Elapsed += (sender, e) => Console.WriteLine($"Tick at {e.SignalTime}");
// 3. 自訂 EventArgs 攜帶資料
public class ElapsedEventArgs : EventArgs
{
public DateTime SignalTime { get; }
public ElapsedEventArgs(DateTime t) => SignalTime = t;
}
關鍵規則:事件只能在宣告類別內部 Invoke,外部僅能 +=/-=,封裝了多播委派的直接呼叫權限。
| 版本 | 語法 | 範例 | 特性 |
|---|---|---|---|
| C# 1.0 | 具名方法 | t = new Transformer(Square); | 需額外方法 |
| C# 2.0 | 匿名方法 | t = delegate(int x) { return x * x; }; | 內嵌程式碼區塊,可捕獲外部變數 |
| C# 3.0 | Lambda 運算式 | t = x => x * x; | 型別推斷、單行隱含 return、支援運算式樹 |
x => x * x — 單一運算式,可轉為 Expression<Func<int,int>> 建構運算式樹 (LINQ to SQL 核心)。x => { Console.WriteLine(x); return x * x; } — 多行程式碼,僅能編譯為委派/匿名方法,不可 轉為運算式樹。var funcs = new List>();
for (int i = 0; i < 3; i++)
funcs.Add(() => i); // 捕獲迴圈變數 i
foreach (var f in funcs) Console.WriteLine(f()); // 全印 3!
// 修正:區域副本
for (int i = 0; i < 3; i++)
{
int captured = i;
funcs.Add(() => captured);
}
// 印出 0, 1, 2
Func<T1..Tn, TResult>:有回傳值 (最多 16 參數)。Action<T1..Tn>:無回傳值。Predicate<T>:Func<T, bool> 別名,常用於 List.FindAll。善用標準委派減少自訂 delegate 宣告,提升程式碼互通性。
委派提供「方法參考」的型別安全封裝,事件在其上建立發布-訂閱契約,Lambda 讓內嵌函數寫法極致簡潔並開啟運算式樹大門。掌握三者關係與演進脈絡,是寫出優雅、可擴充、函數式風格 C# 程式碼的關鍵。