Lambda 運算式

當你想將短運算傳遞給另一個方法時,選擇 lambda 運算式,且該操作在使用該運算的呼叫旁邊最清晰。 內嵌規則會讓讀者看到你如何選擇或轉換項目;命名方法讓他們能先了解操作的目的,再檢查細節。

Tip

本文介紹熟悉 C# 方法與集合的開發者可採用的實務做法。 完整的 lambda 語法與規則,請參見 lambda 表達式的參考資料。

lambda 運算式是一個未命名的函式,你在另一段程式碼需要某種行為的地方撰寫它。

以下 lambda 接受工作階段並回傳該工作階段是否有空缺席次:

session => session.Registered < session.Capacity

運算子 => 將左側參數與右側主體分離。 讀作「goes to」吧。

將行為傳遞給方法

許多 .NET API 接受代理(delegate),這是一種代表特定參數列表與回傳類型行為的型別。 lambda 提供了這種行為。 關於如 Func 和 Action 等代理類型的介紹,請參見 Lambda 表達式、代理與事件。

以下範例適用於工作坊課程。 A WorkshopSession 包含標題、持續時間、容量及註冊數量。

int maximumMinutes = 60;

var shortSessions = sessions
    .Where(session => session.DurationMinutes <= maximumMinutes)
    .OrderBy(session => session.Title);

foreach (WorkshopSession session in shortSessions)
{
    Console.WriteLine(session.Title);
}

第一個 lambda 告訴 Where 你要保留哪些會話。 第二個則是告訴 OrderBy 要依照哪個值排序。 這些方法在不了解任一規則細節的情況下取得行為。

選擇參數形式

選擇最短且保持清晰的參數形式:

session => session.DurationMinutes <= 60

對於一個參數,當編譯器能推斷該型別時,省略括號。 無參數時使用空括號:

() => DateTime.Now

兩個或以上參數請使用括號:

(left, right) => left + right

通常,接收 lambda 的方法會提供足夠的資訊,讓編譯器推斷參數型別。 當型別推斷需要幫助或類型能提升清晰度時,請寫明確的類型:

(WorkshopSession session) => session.Title

lambda 運算式參考涵蓋參數修飾詞、屬性、預設值及其他專門形式。

選擇一個表達式或陳述式主體

當單一運算式可計算結果時,請使用 運算式 Lambda:

session => session.Registered < session.Capacity

當操作需要多個語句時,使用 語句 lambda 。 以大括號括住主體,並在 lambda 回傳值時包含 return :

var descriptions = sessions.Select(session =>
{
    int seatsLeft = session.Capacity - session.Registered;
    string availability = seatsLeft > 0 ? $"{seatsLeft} seats left" : "full";
    return $"{session.Title}: {availability}";
});

Console.WriteLine(string.Join(Environment.NewLine, descriptions));

保持陳述式 lambda 簡潔。 當主體需要多個步驟或同一邏輯重複出現時,請將該行為萃取成局部函數或一般方法,並給它一個描述性的名稱。

從周圍的作用域擷取數值

一個 lambda 可以使用周圍方法中宣告的變數。 這種用法稱為 捕獲。 在第一個例子中,選擇 lambda 擷取 maximumMinutes:

int maximumMinutes = 60;
var shortSessions = sessions.Where(
    session => session.DurationMinutes <= maximumMinutes);

Capture 有助於短規則利用局部脈絡。 lambda 捕捉的是變數本身,而非其值的副本。 如果變數在 lambda 執行前改變,lambda 會看到目前的值。

讓所需的背景資訊容易辨識。 如果 lambda 不需要周圍狀態,請加入 static 以防止意外捕獲,並讓讀者能看到這種獨立性:

var openSessions = sessions.Where(
    static session => session.Registered < session.Capacity);

靜態 lambda 可以使用其參數、其所宣告的變數、常數及靜態成員。 它無法使用周圍程式碼的本地變數或實例狀態。

選擇 lambda、方法群組或局部函式

數種 C# 形式可為委派參數提供行為:

  • 使用 lambda 表達式 來表示簡短行為,置於呼叫旁時仍清楚易讀。
  • 當現有方法具備所需的簽名且名稱能說明行為時,使用 方法群組 。
  • 當包含成員需要可重用的命名邏輯、遞迴,或是對內嵌 lambda 來說太大的實體時,可以使用 本地函式 。
  • 當多名成員需要這種行為時,使用 一般函式。

方法群組是指沒有參數清單的方法名稱。 編譯器會將匹配方法轉換為所需的代理型別:

var openSessions = sessions.Where(HasOpenSeats);

foreach (WorkshopSession session in openSessions)
{
    Console.WriteLine($"Open: {session.Title}");
}

static bool HasOpenSeats(WorkshopSession session) =>
    session.Registered < session.Capacity;

Where(HasOpenSeats) 以較少的程式碼傳達與 Where(session => HasOpenSeats(session)) 相同的規則。 方法名稱告訴讀者規則測試的內容;行內比較顯示它如何測試該規則。 當 lambda 包裝器加入參數、合併操作或使呼叫更清晰時,仍保留 lambda 包裝器。

欲了解詳細的本機比較,請參見本機函數。

也請參閱