習慣 C/C++ 的開發者可能期待逗號運算子在單一運算式中完成多重操作,但 C# 沒有逗號運算子。好在 C# 提供三元運算子 ?:與空合併運算子 ??,讓許多邏輯仍能寫成運算式形式的 Lambda。
?? 處理預設值例如:依參數檔名寫入字串,參數為 null 時用預設檔名:
Action method = filename =>
System.IO.File.WriteAllText(filename ?? "default.txt", "Hello!");
method(null); // 產生 default.txt
method("hello.txt"); // 產生 hello.txt
?: 處理條件分支依旗標決定不同檔名:
Action method = system =>
System.IO.File.AppendAllText(system ? "system.log" : "normal.log", "log message\r\n");
method(false); // 產生 normal.log
method(true); // 產生 system.log
若嘗試用三元運算子替換 if-else 呼叫 void 方法:
Action method = filename =>
filename == null
? Console.WriteLine("Hello!")
: System.IO.File.WriteAllText(filename, "Hello!"); // 編譯錯誤
錯誤原因:三元運算子的第二、三運算元不能是 void 型別運算式,因無法推斷整體運算式型別。但單獨呼叫 void 方法的 Lambda 沒問題:
Action method = filename => System.IO.File.WriteAllText(filename, "Hello!");
非 void 型別則無此限制,例如判斷冬奧年份:
Func method = year =>
year < 1994 ? year % 4 == 0 : year % 4 == 2;
大多數情況 Lambda 參數型別可由委派型別推斷而省略。但有兩種情況必須顯式宣告:
var) 不支援 Lambdavar lambda = (int x) => x * 2; // 錯誤:Lambda 不能指派給隱含型別區域變數
// 正確寫法:
Func lambda = (int x) => x * 2;
當多載方法參數為不同委派型別,編譯器無法判斷該用哪個:
delegate int Delegate1(int x);
delegate int Delegate2(string s);
static void Sample(Delegate1 d) { Console.WriteLine("Delegate1"); }
static void Sample(Delegate2 d) { Console.WriteLine("Delegate2"); }
Sample((int x) => 0); // 正確:指定 int 參數匹配 Delegate1
// Sample((x) => 0); // 錯誤:無法判斷 x 是 int 還是 string
需表示「不執行任何動作」的委派時,慣用空陳述式 Lambda () => { } 取代 null,避免呼叫端需檢查 null:
static void Sample(Action action) {
if (action != null) action("Hello!");
}
Action action = null;
Sample(action); // 什麼也不發生
action = x => Console.WriteLine(x);
Sample(action); // 輸出 Hello!
static void Sample(Action action) {
action("Hello!"); // 直接呼叫,無需 null 檢查
}
Action action = x => { }; // 空 Lambda
Sample(action); // 安全執行,無副作用
action = x => Console.WriteLine(x);
Sample(action); // 輸出 Hello!
此技巧常用於自動化測試:回放使用者操作時,將輸出相關委派替換為空 Lambda,即可禁用輸出且保持程式碼簡潔,大幅提升測試效率。