Enumerable.Aggregate 是 LINQ 中最基礎也最強大的聚合運算,其他聚合方法 (Sum, Max, Min, Average) 皆可視為其特例。本文解析其運作原理、進階應用,並改良加入索引參數擴展功能。
// 基本求和 (不如 Sum 直觀)
int sum = nums.Aggregate((a, b) => a + b);
// 字串串接
string sentence = words.Aggregate((a, b) => a + " " + b);
// 異或校驗和 (串口通訊常用)
byte checksum = data.Aggregate((a, b) => (byte)(a ^ b));
// 簡化版來源碼
public static TSource Aggregate<TSource>(
this IEnumerable<TSource> source,
Func<TSource, TSource, TSource> func)
{
using var enumerator = source.GetEnumerator();
if (!enumerator.MoveNext()) throw new InvalidOperationException("序列為空");
TSource result = enumerator.Current;
while (enumerator.MoveNext())
result = func(result, enumerator.Current);
return result;
}
參數對應:func(累積值, 當前元素) → 回傳新累積值。
string reverse = words.Aggregate((a, b) => b.Reverse() + " " + a);
// 需自訂 Reverse 擴展
public static string Reverse(this string s)
=> new string(s.Reverse().ToArray());
// 1~n 放入 n+1 長度陣列,唯一重複,找出重複數
int[] arr = { 1, 3, 2, 3, 4, 5 };
// 原版:Select + Sum
int dup1 = arr.Select((val, idx) => val - idx).Sum(); // 3
// 改良版:單一 Aggregate
int dup2 = arr.Aggregate((acc, val, idx) => acc + val - idx); // 3
public static class AggregateExtensions
{
// 多載 1: 累積值、當前元素、索引 (從 0 開始)
public static TSource Aggregate<TSource>(
this IEnumerable<TSource> source,
Func<TSource, TSource, int, TSource> func)
{
int index = 0;
using var enumerator = source.GetEnumerator();
if (!enumerator.MoveNext()) throw new InvalidOperationException("序列為空");
TSource result = enumerator.Current;
index++;
while (enumerator.MoveNext())
result = func(result, enumerator.Current, index++);
return result;
}
// 多載 2: 指定種子值、累積值、當前元素、索引
public static TAccumulate Aggregate<TSource, TAccumulate>(
this IEnumerable<TSource> source,
TAccumulate seed,
Func<TAccumulate, TSource, int, TAccumulate> func)
{
int index = 0;
TAccumulate result = seed;
foreach (var item in source)
result = func(result, item, index++);
return result;
}
// 多載 3: 完整版 (種子、累積函式、結果選擇器、索引)
public static TResult Aggregate<TSource, TAccumulate, TResult>(
this IEnumerable<TSource> source,
TAccumulate seed,
Func<TAccumulate, TSource, int, TAccumulate> func,
Func<TAccumulate, TResult> resultSelector)
{
return resultSelector(source.Aggregate(seed, func));
}
}
// 間隔求和:索引為偶數的元素相加 (0, 2, 4...)
int[] nums = { 10, 20, 30, 40, 50 };
int evenSum = nums.Aggregate((acc, val, idx) => acc + (idx % 2 == 0 ? val : 0)); // 10+30+50=90
// 計算加權平均
var weightedAvg = items.Aggregate(
(Sum: 0.0, Weight: 0.0),
(acc, item, idx) => (acc.Sum + item.Value * item.Weight, acc.Weight + item.Weight),
acc => acc.Sum / acc.Weight);
// 字串插值建構
string csv = items.Aggregate(
"",
(acc, item, idx) => acc + (idx > 0 ? "," : "") + item.Name);
| 場景 | 建議 |
|---|---|
| 簡單求和/最大/最小/平均 | 直接用 Sum/Max/Min/Average (可讀性最佳) |
| 字串串接 | 用 string.Join 或 StringBuilder (效能優於 Aggregate) |
| 複雜累積邏輯需索引 | 使用改良版 Aggregate |
| 多欄位累積 | 使用 Tuple/ValueTuple 作為種子值 |
| 空序列處理 | 使用帶種子值的多載避免例外 |
Aggregate 是函數式聚合的核心構建塊。理解其運作機制、善用種子值與索引參數,能以極簡代碼表達複雜聚合邏輯。改良版擴展方法填補了原生 API 缺乏索引參數的缺口,推薦收納於專案工具庫中。