CodeGym /課程 /C# SELF /Lambda 表達式語法 ( =>)

Lambda 表達式語法 ( =>)

C# SELF
等級 49 , 課堂 1
開放

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. 回傳 voidAction

當 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

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION