LINQ 提供豐富的聚合運算子,對應 SQL 聚合函數,支援記憶體集合 (LINQ to Objects) 與資料庫查詢 (LINQ to SQL) 雙重場景。本文系統整理六大核心方法語法、行為差異與進階用法。
| 方法 | SQL 對應 | 回傳型別 | 說明 |
|---|---|---|---|
Count / LongCount | COUNT(*) | int / long | 元素總數或符合條件數量 |
Min / Max | MIN() / MAX() | TSource / TResult | 最小/最大值,支援選擇器投影 |
Sum / Average | SUM() / AVG() | 數值型別 (decimal/double) | 總和/平均,需數值型別 |
Aggregate | 無直接對應 | TAccumulate / TResult | 自訂累積演算法 (LINQ to SQL 不支援) |
// 基本計數
int[] nums = { 5, 6, 7 };
int cnt = nums.Count(); // 3
// 帶斷言計數
string s = "jame921";
int digitCnt = s.Count(c => char.IsDigit(c)); // 3
// LongCount:相同功能,回傳 long,適用 > 20 億元素集合
long bigCnt = largeCollection.LongCount();
實作細節: Count() 內部優先檢查 ICollection<T> 介面,若實作則直接讀取 Count 屬性 (O(1)),否則逐一枚舉計數 (O(N))。
int[] numbers = { 28, 32, 14 };
int min = numbers.Min(); // 14
int max = numbers.Max(); // 32
// 選擇器投影:比較個位數
int minDigit = numbers.Min(n => n % 10); // 2 (14%10=4, 28%10=8, 32%10=2) → 2
int maxDigit = numbers.Max(n => n % 10); // 8
// 複雜型別必須提供選擇器 (未實作 IComparable)
var cheapestPrice = ctx.Purchases.Min(p => p.Price); // OK: decimal?
// var cheapestOrder = ctx.Purchases.Min(); // 編譯錯誤:Purchase 無 IComparable
// 取得最便宜訂單完整物件:需子查詢
var cheapestOrder = ctx.Purchases
.Where(p => p.Price == ctx.Purchases.Min(p2 => p2.Price))
.FirstOrDefault();
decimal[] vals = { 3, 4, 8 };
decimal sum = vals.Sum(); // 15
decimal avg = vals.Average(); // 5
// 選擇器:字串長度總和
string[] names = { "Tom", "Jerry" };
int totalLen = names.Sum(s => s.Length); // 3+5=8
// 型別規則嚴格:
// - Sum/Average 僅支援數值型別 (int, long, float, double, decimal 及其 nullable)
// - Min/Max 支援任何實作 IComparable 的型別 (含 string)
// Average 回傳型別規則:
// 1. 選擇器為 decimal → 回傳 decimal
// 2. 選擇器為 int/long/float/double → 回傳 double
// 因此:
// int avg = new int[]{3,4}.Average(); // 編譯錯誤:double 無法隱含轉換 int
double avg = new int[]{3,4}.Average(); // 3.5 ✓
// 精度提升:Average 內部以 double/decimal 累加避免溢位/精度損失
double avg2 = numbers.Average(n => (double)n); // 同結果
// 找出平均訂單金額 > 500 的客戶名稱
var q = from c in ctx.Customers
where c.Purchases.Average(p => p.Price) > 500
select c.Name;
// 翻譯為 SQL:AVG(Price) 配合 GROUP BY / HAVING
適用於 LINQ to Objects (記憶體集合) 的複雜累積邏輯,**LINQ to SQL 不支援** (無法翻譯為 SQL)。
// 等同於 Sum 的自訂實現
int[] numbers = { 1, 2, 3 };
int sum = numbers.Aggregate(0, (seed, n) => seed + n); // 6
// 參數解析:
// 1. seed (種子/初始值) = 0
// 2. func (累積函數) = (目前累積值, 下一個元素) => 新累積值
// 3. 可選第三參數 resultSelector:最終投影
// 進階:計算標準差 (兩次遍歷或單次雙累積)
double mean = numbers.Average();
double stdDev = Math.Sqrt(numbers.Average(x => Math.Pow(x - mean, 2)));
Aggregate (僅 LINQ to Objects)。Aggregate 導致客戶端評估效能災難。Average 回傳 double/decimal,切勿隱含轉換為 int。