1. 介紹
你可能不知道,但面試時最愛問的一題就是「你對 lambda 表達式了解多少?」為啥會問?因為它在 C# 裡超方便又很「潮」,能寫更少、表達更清楚的程式碼。如果把匿名方法比作手寫信,lambda 就像用即時通訊發訊息:又快又精簡,有時還會帶表情(嗯,差不多)。
lambda 表達式從 C# 3.0 開始加入,從此無處不在:在 LINQ、事件處理、執行緒、集合,甚至 .NET 的 AI 函式庫都常用到。
lambda 表達式 是在程式碼裡「就地」宣告方法的一種方式,沒有名字,超簡潔明瞭。基本語法長這樣:
(參數) => 表達式_或_程式區塊
這個「箭頭」 => 通常叫做 lambda operator,或簡單說就是「箭頭」。
小小比喻
想像寫食譜:「拿顆蘋果、切一切、丟進碗裡」。
在 C# 裡會像:
(蘋果) => 切(蘋果)
2. 把匿名方法改寫成 lambda
假設我們有個 delegate:
delegate int SquareDelegate(int x);
匿名方法會長這樣:
SquareDelegate sq = delegate(int x) {
return x * x;
};
現在用 lambda 表達式寫法:
SquareDelegate sq = (int x) => { return x * x; };
不過 C# 會推斷型別,所以還能更簡潔:
SquareDelegate sq = x => x * x;
真是簡潔到不行!
和「一般」方法與匿名函式比較
| 方式 | 宣告位置 | 可以在哪用 | 缺點 |
|---|---|---|---|
| 具名方法 | 在類別裡 | 任何地方 | 需要名字、程式碼較多 |
| 匿名方法 | 在程式碼中 | 只能搭配 delegate 使用 | 語法比較冗長 |
| lambda 表達式 | 在程式碼中 | 搭配 delegate 幾乎都能用 | 有時型別不明顯 |
3. Lambda 語法:幾種變化
參數
無參數:
Action hello = () => Console.WriteLine("哈囉,世界!");
單個參數(可以省略括號):
Func<int, int> inc = x => x + 1;
多個參數(需要括號):
Func<int, int, int> sum = (a, b) => a + b;
Lambda 主體
單一表達式 — 不用大括號也不用 return:
x => x * x
程式區塊 — 要用大括號,並需要 return:
(x, y) =>
{
int z = x + y;
return z * z;
}
參數型別
大部分情況不用寫型別—編譯器會推斷。但要寫也可以:
(x, y) => x + y // 編譯器會自行推斷型別.
(int x, int y) => x + y // 可以明確指定.
4. 注意事項與更實際的範例
和集合一起使用
還記得我們的小教學範例(例如使用者清單)嗎?假設有個整數陣列:
int[] numbers = { 1, 2, 3, 4, 5 };
要挑出偶數:
var evenNumbers = numbers.Where(n => n % 2 == 0);
這裡 Where 是 LINQ 的 extension method,條件就是我們的 lambda 篩選器。
傳入方法
假設我們有個 delegate 方法:
public delegate bool Filter(int number);
public static int[] FilterNumbers(int[] data, Filter predicate)
{
var result = new List<int>();
foreach (var n in data)
if (predicate(n))
result.Add(n);
return result.ToArray();
}
現在傳入 lambda:
int[] evens = FilterNumbers(numbers, n => n % 2 == 0);
把 lambda 當成事件處理器
button.Click += (sender, args) => Console.WriteLine("按鈕被點擊!");
5. Lambda 與標準泛型 delegate
沒辦法想像 lambda 不搭配 Func<>, Action<> 和 Predicate<>。它們是特殊的 delegate 類型:
- Action — 不回傳值。
- Func — 回傳值。
- Predicate — 回傳 bool,通常用來過濾。
Action 範例:
Action<string> log = message => Console.WriteLine(message);
log("這是透過 lambda 的訊息!");
Func 範例:
Func<int, int, int> multiply = (a, b) => a * b;
int product = multiply(3, 5); // 15
Predicate 範例:
Predicate<int> isNegative = n => n < 0;
bool test = isNegative(-7); // true
6. 多表達式的 lambda:什麼時候要用區塊
如果 lambda 要做多個步驟,就用大括號並明確寫 return:
Func<int, int, string> describeSum = (a, b) =>
{
int sum = a + b;
return $"總和: {sum}";
};
沒有 return 的話編譯器會抗議(但不會說得很清楚為什麼——通常只會說「並非所有路徑都有回傳值」)。
7. 回傳 void:Action
當 lambda 不回傳值,就用 Action:
Action greet = () => Console.WriteLine("笑一個 — 程式碼能編譯!");
lambda 可以包含任意多的敘述:
Action<int> printSquare = x =>
{
int sq = x * x;
Console.WriteLine($"數字 {x} 的平方是 {sq}");
};
8. 限制與常見錯誤
有時你想超彈性寫 lambda,但編譯器不一定買單。
不能宣告跟外層捕獲變數同名的變數。
注意型別:若 delegate 的簽章和 lambda 不吻合,就會出錯。
如果 delegate 要求回傳值,那就一定要回傳。
在比較複雜的 lambda 裡別忘了大括號。單一表達式不用括號也不用 return。多個敘述就要括號和 return。
GO TO FULL VERSION