Lambda 表達式是 C# 3.0 引入的關鍵語法糖,本質是匿名方法的簡潔表示,更是通往函數式程式設計的大門。本文將從委派關係、參數形式、單行/多行寫法、外部變數捕獲(閉包)四大面向,系統梳理 Lambda 的核心用法與底層機制。
只要方法簽名接受委派型別,即可傳入 Lambda:
Func lambda = param =>
{
param += ", middle part,";
param += " and this was added to the string";
return param;
};
Console.WriteLine(lambda("Start of string"));
// 輸出: Start of string, middle part, and this was added to the string
=> 左側:輸入參數(型別可推斷省略)。=> 右側:方法本體(單行可省略大括號與 return)。| 委派簽名 | Lambda 寫法 | 說明 |
|---|---|---|
Func<T, TResult> | x => x.ToString() | 單一參數,括號可省略 |
Func<T1, T2, TResult> | (x, y) => x + y | 多參數必須加括號 |
Action<T1, T2> | (x, y) => Console.WriteLine(x + y) | 無回傳值 |
| 具名參數 | (int x, string s) => ... | 必要時可顯式標註型別 |
// 單行運算式:隱含 return,無大括號
Func add = (a, b) => a + b;
Func upper = s => s.ToUpper();
// 多行陳述式:需大括號與明確 return
Func addLog = (a, b) =>
{
Console.WriteLine($"Adding {a} + {b}");
return a + b;
};
int factor = 5;
Func multiply = x => x * factor; // 捕獲 factor
factor = 10;
Console.WriteLine(multiply(3)); // 輸出 30,非 15!
關鍵行為:Lambda 捕獲的是變數本身,而非建立時的值。後續修改外部變數,Lambda 內部見到的即是新值。
// 編譯器自動產生
class __Closure
{
public int factor; // 被捕獲的變數升級為欄位
public int Multiply(int x) => x * factor;
}
// 使用時
var closure = new __Closure { factor = 5 };
Func multiply = closure.Multiply;
closure.factor = 10; // 影響後續呼叫
這就是為何 foreach 迴圈變數捕獲易出錯(所有 Lambda 共用同一迴圈變數),解決方案是在迴圈內建立區域副本:
foreach (var item in items)
{
var captured = item; // 區域副本
list.Add(() => Console.WriteLine(captured));
}
當 Lambda 指派給 Expression<Func<T, TResult>> 時,編譯器產生運算式樹而非 IL,支援 LINQ to SQL、動態查詢建構等進階場景:
Expression> expr = x => x > 5;
// 可在執行時解析、修改、轉譯為 SQL
Lambda 不僅是語法糖,更是將「函數作為一等公民」在 C# 中落地的關鍵。掌握參數推斷、閉包語意、運算式樹區別,能讓你寫出更簡潔、更具表達力的程式碼,並為並行計算、領域特定語言等現代開發範式奠基。