首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

LINQ 擴展方法深度解析:為什麼標準庫沒有 DistinctBy 與 ForEach?

     編輯:關於.NET

前言

開發者常感困惑:LINQ 提供 Distinct 卻無 DistinctBy (依鍵去重);提供 Select 卻無 ForEach (副作用迭代)。本文從語言設計哲學、效能、組合性角度解析微軟的決策邏輯,並給出正確的替代方案。

一、DistinctBy:其實已內建於組合查詢中

Distinct 僅支援完整物件比較 (需實作 IEquatable<T> 或傳入 IEqualityComparer<T>),無法直接「依屬性去重」。但透過現有運算子組合即可達成:

var persons = new[] {
    new Person{Age=10,Name="a"}, new Person{Age=10,Name="b"},
    new Person{Age=10,Name="c"}, new Person{Age=20,Name="d"}
};
foreach (var p in persons.GroupBy(o => o.Age).Select(g => g.First())) {
    Console.WriteLine(p); // Age=10 (a), Age=20 (d)
}

原理:GroupBy(keySelector) 分組 → Select(g => g.First()) 取每組第一筆。此組合即為 DistinctBy 語義。

封裝為擴展方法 (方便重用)

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
    this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) {
    return source.GroupBy(keySelector).Select(g => g.First());
}

二、ForEach:刻意缺席的設計哲學

微軟不在 IEnumerable<T> 提供 ForEach,主要基於三大理由:

  1. 破壞鏈式組合:ForEach 回傳 void,無法接續後續運算子 (如 .Where(...).ForEach(...).Select(...)),違反 LINQ 流式設計。
  2. 破壞延遲執行:LINQ 核心價值在於延遲執行 (建立查詢計畫而非立即執行)。ForEach 強制立即迭代執行副作用,喪失組合優化機會。
  3. 副作用與查詢分離:LINQ 設計為「純查詢轉換」,副作用 (I/O、UI 更新、狀態變更) 應在查詢物質化後 (如 ToList()) 以 foreach 陳述式明確處理,語意更清晰。

常見錯誤實作 (回傳 void)

public static void ForEach<T>(this IEnumerable<T> source, Action<T> action) {
    foreach (var t in source) action(t); // 破壞鏈式、破壞延遲
}

勉強修正版 (回傳 source 保留鏈式,但仍破壞延遲)

public static IEnumerable<T> ForEach<T>(this IEnumerable<T> source, Action<T> action) {
    foreach (var t in source) action(t);
    return source; // 仍會立即執行 action,喪失延遲特性
}

三、最佳實踐建議

  • DistinctBy:直接使用 GroupBy(...).Select(g => g.First()) 或引用 MoreLINQ 套件 (DistinctBy 經過充分測試)。
  • ForEach:不要 寫擴展方法。乖乖用 foreach (var item in query) { ... }。若需在管線中執行副作用並保留延遲,可考慮 Do (Reactive Extensions) 或自訂 IEnumerable<T> 迭代器,但通常 ToList().ForEach 或 foreach 已足夠。

總結

標準 LINQ 缺少 DistinctBy/ForEach 非疏漏,而是刻意設計:前者可由現有運算子零成本組合,後者為避免破壞「延遲執行、鏈式組合、純查詢」三大核心價值。理解設計意圖,才能寫出地道、高效、可維護的 LINQ 代碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved