CodeGym /課程 /C# SELF /聚合函式: Sum、

聚合函式: Sum、 Count、 Average、 Max、 Min

C# SELF
等級 32, 課堂 1
開放

1. 前言

在處理集合時,常常不只是要一個一個元素處理,而是想要拿到整個資料集的「摘要」。比如說:

  • 算一下有多少學生拿到五分。
  • 知道學校裡總共有多少學生。
  • 算出所有學生考試總共拿了多少分。
  • 找出最高分和最低分。
  • 算出全班的平均分數。

當然啦,這些都可以用一般 for 迴圈加個計數器來解決。但說真的,誰想為了這種小事寫二十行 code 啦?

LINQ 給你一組聚合函式——現成的方法,直接把集合丟進去,幫你跑過所有元素,直接給你答案:總和、平均、最大、最小,有時還能做更酷的事。

這裡有個「聚合函式速查表」:

方法 功能 回傳值
Count()
計算元素數量
int
Sum()
計算數值總和 依元素型別而定(
int
、
double
...)
Average()
計算平均值
double
或其他數值型別
Max()
找出最大元素 集合中的元素
Min()
找出最小元素 集合中的元素

這些方法都是給有實作 IEnumerable<T> 的集合用的 extension method。更多細節可以看LINQ 聚合方法官方文件。

2. 準備練習用的資料

我們繼續用一個簡單的學生管理小程式。假設有這樣一個 class:


// 學生模型
public class Student
{
    public string Name { get; set; }
    public int Grade { get; set; } // 五分制成績
    public string Email { get; set; }
}

還有一個 list:


// 學生基本清單
List<Student> students = new List<Student>
{
    new Student { Name = "伊凡", Grade = 5, Email = "ivan@example.com" },
    new Student { Name = "奧莉嘉", Grade = 4, Email = "olga@example.com" },
    new Student { Name = "阿爾喬姆", Grade = 3, Email = "artem@example.com" },
    new Student { Name = "達莉亞", Grade = 5, Email = "darya@example.com" },
    new Student { Name = "彼得", Grade = 2, Email = "petr@example.com" }
};

3. 計算元素數量:Count()

最基本的統計

假設你想知道總共有幾個學生:


int totalStudents = students.Count(); // 5
Console.WriteLine($"總學生數:{totalStudents}");

LINQ 魔法:超簡單!Count() 直接回傳集合裡的元素數量。

條件計數

那班上有幾個優等生?


int excellentStudents = students.Count(s => s.Grade == 5);
Console.WriteLine($"五分學生數:{excellentStudents}");

這裡我們給 Count 傳一個 lambda——只會算出符合條件(s.Grade == 5)的學生。在 LINQ 裡這等於 Where(...).Count(),但寫法更短也更有效率。

如果集合是空的?

如果集合是空的,Count 會老實回傳 0——不會報錯。

4. 數值加總:Sum()

算出所有成績總和

假設你想知道全班總共拿了多少分:


int sumOfGrades = students.Sum(s => s.Grade); // 5+4+3+5+2 = 19
Console.WriteLine($"所有成績總和:{sumOfGrades}");

Sum 會吃一個 selector(lambda),每個元素回傳一個值。

直接對數字集合加總

如果你只有一個數字 array,selector 就不用寫:


int[] numbers = { 1, 2, 3, 4, 5 };
int sum = numbers.Sum(); // 15

常見錯誤和細節

如果集合是空的,數值型別的 Sum() 會回傳 0。但如果集合是 nullable 型別(int?、double?),Sum 也會正常運作:null 會被忽略。

5. 平均值:Average()

算出班級平均分

經典問題:學生平均拿幾分?


double averageGrade = students.Average(s => s.Grade); // (5+4+3+5+2)/5 = 3.8
Console.WriteLine($"平均分數:{averageGrade:F2}");

Average 超適合做統計。注意:回傳值永遠是 double,就算原本是 int,這樣才不會把小數點砍掉。

如果沒有任何元素

如果集合是空的,呼叫 Average() 會丟出 InvalidOperationException。這超常見!如果你不確定集合有沒有東西,記得先檢查一下!


if (students.Any())
    Console.WriteLine(students.Average(s => s.Grade));
else
    Console.WriteLine("沒有資料可以算平均!");

數字 array 的平均值


double avg = numbers.Average();

6. 最大值和最小值:Max() 和 Min()

誰是班上之星,誰在墊底

想知道誰把全班分數拉高,誰讓老師頭痛?很簡單:


int maxGrade = students.Max(s => s.Grade); // 5
int minGrade = students.Min(s => s.Grade); // 2

Console.WriteLine($"最高分:{maxGrade}");
Console.WriteLine($"最低分:{minGrade}");

那這個英雄到底是誰?

有時候不只要分數,還想知道是誰拿的。找出最高分的學生物件:


// 先照分數遞減排序,拿第一個
var bestStudent = students.OrderByDescending(s => s.Grade).First();
Console.WriteLine($"最強學生:{bestStudent.Name} ({bestStudent.Grade})");

新版 .NET 有 MaxBy,可以更優雅地寫。從 .NET 6 開始有,.NET 9 又更方便(看MaxBy 官方文件)。但就算沒 MaxBy,用排序加 .First() 也很夠用啦。

陷阱和差異

如果集合是空的,呼叫 Max() 和 Min() 也會丟 InvalidOperationException。所以最好先檢查一下(尤其你沒先檢查集合的話):


if (students.Any())
    Console.WriteLine($"最高分:{students.Max(s => s.Grade)}");
else
    Console.WriteLine("沒有學生可以找最大值。");

7. 聚合方法「串接」範例

聚合方法常常會跟篩選、投影一起用:


// 五分學生的平均分(當然永遠是 5,但只是舉例)
double avgExcellent = students
    .Where(s => s.Grade == 5)
    .Average(s => s.Grade);

// 分數不低於 4 的學生總分
int sumGood = students
    .Where(s => s.Grade >= 4)
    .Sum(s => s.Grade);

// 不重複 email 數量(以防萬一)
int uniqueEmails = students
    .Select(s => s.Email)
    .Distinct()
    .Count();

這時 LINQ 就超有感:簡單組合操作,寫出來的 code 超直觀,連你家的貓都看得懂(如果你家貓是 Junior C# Developer 的話)。

8. 跟「手動」寫法比:為什麼要用聚合?

我們來對比一下 LINQ 跟傳統 for 迴圈,拿平均分數來舉例:

傳統寫法:


int sum = 0;
int count = 0;
foreach (var s in students)
{
    sum += s.Grade;
    count++;
}
double average = (count != 0) ? (double)sum / count : 0;

LINQ:


double average = students.Average(s => s.Grade);

就算要處理空集合,也更簡單!

9. 有用的小細節

簡單聊聊效能和實作特性

LINQ 聚合方法跟大多數 LINQ 操作不一樣,是馬上執行的!也就是說你呼叫 Sum()、Count()、Average()、Max()、Min() 時,集合就會直接被跑一遍。這些方法回傳的不是集合,而是一個最終結果。

這很重要:如果你在聚合前做了很重的操作,比如複雜的篩選或轉換——這些只會在聚合呼叫時執行一次。

聚合方法支援 query-syntax 嗎?

新手常問:「這些能不能用 query-syntax 寫?」簡單說:query-syntax 本身沒聚合關鍵字,但你可以跟 method syntax 混用:


var avg = (from s in students where s.Grade > 3 select s.Grade).Average();

括號裡是 query-syntax,然後直接呼叫聚合方法。這種寫法其實很常見!

10. LINQ 常見錯誤

錯誤一:對空集合用 Average()、Max() 或 Min()。
如果集合是空的,Sum() 和 Count() 會乖乖回傳 0,但 Average()、Max()、Min() 會丟例外。呼叫這些方法前,記得先確定集合有東西。

錯誤二:lambda 寫錯型別。
比如你傳了字串給聚合函式(Sum、Max 等),會直接編譯錯誤。用匿名方法時特別容易出錯。

錯誤三:用不夠優的方式算數量。
Where(...).Count() 會先產生新集合再算數量。其實用 Count(predicate) 就好,直接計算符合條件的數量,效率更高。

錯誤四:聚合 nullable 型別時沒注意細節。
如果你對 int? 用 Sum(),Sum() 會自動忽略 null。這是正常行為,但有時你沒注意到,結果會跟你想的不一樣。

2
任務
C# SELF, 等級 32, 課堂 1
上鎖
計算陣列元素的總和
計算陣列元素的總和
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION