C# GroupBy操作符详解 – 对数据进行分组和聚合

1次阅读

groupby是linq中用于按指定键分组的核心操作符,返回ienumerable,支持单属性、多级、复合键分组及自定义比较器,并可结合select实现聚合计算。

C# GroupBy操作符详解 – 对数据进行分组和聚合

GroupBy 是 LINQ 中最常用也最强大的操作符之一,它能将集合按指定键(key)分组,生成一个“组的集合”,每组内部包含具有相同键的所有元素。它不是简单地去重或排序,而是为后续聚合(如求和、计数、平均值)打下基础。

GroupBy 的基本用法:按单个属性分组

最常见场景是按对象的某个属性(如 Category、Status、Year)分组。返回类型是 IEnumerable<igrouping tsource>></igrouping>,其中每个 IGrouping 既是一个分组,本身也继承IEnumerable<tsource></tsource>,可直接遍历其元素。

示例:按产品类别分组

var products = new List<Product> {     new Product { Name = "苹果", Category = "水果", Price = 5.0 },     new Product { Name = "香蕉", Category = "水果", Price = 3.5 },     new Product { Name = "牛奶", Category = "乳制品", Price = 8.0 } }; <p>var grouped = products.GroupBy(p => p.Category);</p><p>foreach (var group in grouped) { Console.WriteLine($"类别:{group.Key}"); foreach (var p in group) // group 本身可枚举 Console.WriteLine($"  - {p.Name} ({p.Price:C})"); }</p>

带聚合的 GroupBy:count、Sum、Average 等一步到位

单纯分组后还需手动遍历计算?没必要。可以在 GroupBy 后接投影(Select),对每个分组做聚合操作,直接得到结构化结果。

  • 统计每类商品数量:.Select(g => new { Category = g.Key, Count = g.Count() })
  • 计算每类平均价格:.Select(g => new { Category = g.Key, AvgPrice = g.Average(p => p.Price) })
  • 获取每类最高价商品:.Select(g => g.OrderByDescending(p => p.Price).First())

注意:聚合方法(如 Count()Average())作用在 g(即 IGrouping)上,它已经是同键元素的集合。

多级分组与复合键:用匿名类型或元组构造 Key

需要按多个字段联合分组(如“年份 + 类别”)时,不能写 GroupBy(x => x.Year, x => x.Category)——那是错误语法。正确做法是让 key 成为一个组合对象:

  • 用匿名类型:.GroupBy(x => new { x.Year, x.Category })
  • 用 ValueTuple(推荐,更轻量):.GroupBy(x => (x.Year, x.Category))

此时 g.Key.Yearg.Key.Category 可直接访问;投影时也能自然解构,比如 .Select(g => new { g.Key.Year, g.Key.Category, Total = g.Sum(p => p.Sales) })

自定义相等比较器:当默认比较不满足需求时

默认情况下,GroupBy 对 key 使用引用/值类型的默认相等逻辑。若需按忽略大小写、四舍五入、或自定义规则分组,可传入 IEqualityComparer<tkey></tkey> 实现。

例如:按价格四舍五入到整数分组

public class RoundedPriceComparer : IEqualityComparer<decimal> {     public bool Equals(decimal x, decimal y) => Math.Round(x) == Math.Round(y);     public int GetHashCode(decimal obj) => Math.Round(obj).GetHashCode(); } <p>var groupedByRoundedPrice = products.GroupBy(p => p.Price, new RoundedPriceComparer());</p>

注意:自定义比较器必须同时实现 EqualsGetHashCode,且逻辑保持一致。

基本上就这些。GroupBy 看似简单,但灵活组合 key 选择、投影和聚合,就能应对绝大多数报表、统计、数据透视类需求。不复杂但容易忽略细节——尤其是 key 的类型稳定性与比较逻辑的一致性。

text=ZqhQzanResources