當你想將短運算傳遞給另一個方法時,選擇 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 包裝器。
欲了解詳細的本機比較,請參見本機函數。