当前位置: 首页 > news >正文

LINQ 新时代:CountBy、AggregateBy 深度解析(含对比 GroupBy)

简介

.NET 8之前,LINQ没有内置CountByAggregateBy方法,但在.NET 9(C# 13)中,LINQ正式引入了这两个新扩展方法,极大简化了数据分组和聚合的写法。

背景

传统的分组统计一般使用GroupBy

varquery=list.GroupBy(x=>x.Category).Select(g=>new{Category=g.Key,Count=g.Count()});

GroupBy

  • 代码冗长

  • 对简单的计数/聚合任务过于复杂

为此,.NET 9引入:

  • CountBy→ 按键快速计数

  • AggregateBy→ 按键快速聚合

什么是 CountBy 和 AggregateBy?
  • CountBy:用于按键(key)对集合进行分组并统计每个键的出现次数,返回一个键值对集合,其中键是分组依据,值是该键的计数。

  • AggregateBy:用于按键对集合进行分组并对每个分组应用自定义聚合函数,返回一个键值对集合,其中键是分组依据,值是聚合结果。

这两个方法类似于GroupBy后接CountAggregate,但它们更高效、更简洁,减少了中间分组对象的创建,优化了性能。

关键特点:

  • 高效性:直接生成键值对结果,避免GroupBy创建中间IGrouping对象的开销。

  • 简洁性:将分组和统计/聚合合并为一步操作。

  • 灵活性:支持自定义键选择器和聚合逻辑。

  • 返回类型:返回IEnumerable<KeyValuePair<TKey, TValue>>,便于进一步处理。

CountBy

作用:按键分组并统计数量。
类似GroupBy(...).Select(...g.Count())的简化版。

方法签名

publicstaticIEnumerable<KeyValuePair<TKey,int>>CountBy<TSource,TKey>(thisIEnumerable<TSource>source,Func<TSource,TKey>keySelector,IEqualityComparer<TKey>?comparer=null)
  • source:输入的集合(实现IEnumerable<TSource>)。

  • keySelector:一个函数,从每个元素提取分组的键。

  • comparer:可选的键比较器,用于自定义键的相等性判断(默认使用EqualityComparer<TKey>.Default)。

  • 返回:IEnumerable<KeyValuePair<TKey, int>>,每个键值对包含键和该键的计数。

基础用法
varfruits=new[]{"apple","banana","apple","orange","banana","apple"};varresult=fruits.CountBy(f=>f);foreach(varkvinresult){Console.WriteLine($"{kv.Key}:{kv.Value}");}

输出:

apple: 3 banana: 2 orange: 1

等价于:

fruits.GroupBy(f=>f).Select(g=>newKeyValuePair<string,int>(g.Key,g.Count()));
  • fruit => fruit按水果名称分组并计数。

  • 结果是一个键值对集合,键是水果名称,值是出现次数。

自定义键
varnumbers=new[]{1,2,3,4,5,6};varresult=numbers.CountBy(n=>n%2==0?"Even":"Odd");

输出:

Odd: 3 Even: 3
使用比较器:忽略大小写
varnames=new[]{"apple","Apple","APPLE","banana"};varcounts=names.CountBy(name=>name,StringComparer.OrdinalIgnoreCase);foreach(varkvpincounts){Console.WriteLine($"{kvp.Key}:{kvp.Value}");}// 输出:// apple: 3// banana: 1
  • StringComparer.OrdinalIgnoreCase忽略键的大小写。

AggregateBy

作用:按键分组并在分组中执行自定义聚合逻辑(不仅仅是计数)。
类似GroupBy(...).Aggregate(...)的简化版。

方法签名

publicstaticIEnumerable<KeyValuePair<TKey,TResult>>AggregateBy<TSource,TKey,TAccumulate,TResult>(thisIEnumerable<TSource>source,Func<TSource,TKey>keySelector,TAccumulateseed,Func<TAccumulate,TSource,TAccumulate>func,Func<TKey,TAccumulate,TResult>resultSelector,IEqualityComparer<TKey>?comparer=null)

参数说明:

  • source:输入的集合(实现IEnumerable<TSource>)。

  • keySelector:从每个元素提取分组的键。

  • seed:聚合的初始值(每个分组从此值开始)。

  • func:聚合函数,定义如何将元素累加到当前累积值。

  • resultSelector:结果选择器,将键和最终累积值转换为结果。

  • comparer:可选的键比较器。

  • 返回:IEnumerable<KeyValuePair<TKey, TResult>>,每个键值对包含键和聚合结果。

求和
varorders=new[]{new{Category="Book",Price=10},new{Category="Book",Price=20},new{Category="Food",Price=5},new{Category="Food",Price=7},};varresult=orders.AggregateBy(keySelector:o=>o.Category,seed:0m,accumulator:(sum,item)=>sum+item.Price);foreach(varkvinresult){Console.WriteLine($"{kv.Key}:{kv.Value}");}

输出:

Book: 30 Food: 12

等价于:

orders.GroupBy(o=>o.Category).Select(g=>newKeyValuePair<string,decimal>(g.Key,g.Sum(x=>x.Price)));
拼接字符串
varnames=new[]{new{Group="A",Name="Alice"},new{Group="A",Name="Alex"},new{Group="B",Name="Bob"},};varresult=names.AggregateBy(keySelector:n=>n.Group,seed:"",accumulator:(s,n)=>s==""?n.Name:$"{s},{n.Name}");foreach(varkvinresult){Console.WriteLine($"{kv.Key}:{kv.Value}");}

输出:

A:Alice,AlexB:Bob
使用自定义结果:统计最大值
varitems=new[]{new{Category="A",Value=10},new{Category="B",Value=20},new{Category="A",Value=15}};varmaxValues=items.AggregateBy(item=>item.Category,// 按类别分组seed:int.MinValue,// 初始值为最小整数(max,item)=>Math.Max(max,item.Value),// 取最大值(key,max)=>max);// 返回最大值foreach(varkvpinmaxValues){Console.WriteLine($"{kvp.Key}:{kvp.Value}");}// 输出:// A: 15// B: 20

CountBy vs AggregateBy

特性CountByAggregateBy
功能仅计数自定义任何聚合操作
返回类型IEnumerable<KeyValuePair<TKey,int>>IEnumerable<KeyValuePair<TKey,TAccumulate>>
复杂度更简洁更灵活,但需提供 seed 和 accumulator
适用场景频率统计求和、平均值、拼接字符串、自定义聚合等

性能优势

GroupBy相比:

  • CountBy / AggregateBy只执行一次遍历

  • 内部使用 哈希表累积,减少对象创建

  • 对大数据集统计效率更高

实战示例:日志统计

recordLog(stringLevel,intSize);varlogs=new[]{newLog("Info",10),newLog("Error",5),newLog("Info",20),newLog("Error",15),newLog("Warning",7)};// 统计不同 Level 的日志数量varcount=logs.CountBy(l=>l.Level);// 统计不同 Level 的总 Sizevarsize=logs.AggregateBy(l=>l.Level,0,(sum,log)=>sum+log.Size);

输出:

---Count--- Info: 2 Error: 2 Warning: 1 ---Size--- Info: 30 Error: 20 Warning: 7
统计单词频率并排序
vartext="the quick brown fox jumps over the lazy dog the quick fox";varwords=text.Split(' ');varwordCounts=words.CountBy(word=>word,StringComparer.OrdinalIgnoreCase).OrderByDescending(kvp=>kvp.Value);foreach(varkvpinwordCounts){Console.WriteLine($"{kvp.Key}:{kvp.Value}");}// 输出:// the: 3// quick: 2// fox: 2// brown: 1// jumps: 1// over: 1// lazy: 1// dog: 1
复杂聚合(构建对象)
varorders=new[]{new{Customer="Alice",Amount=100,Item="Laptop"},new{Customer="Bob",Amount=50,Item="Mouse"},new{Customer="Alice",Amount=200,Item="Phone"}};varsummaries=orders.AggregateBy(order=>order.Customer,seed:new{Total=0,Items=newList<string>()},(acc,order)=>new{Total=acc.Total+order.Amount,Items=acc.Items.Append(order.Item).ToList()},(key,acc)=>new{Customer=key,acc.Total,acc.Items});foreach(varsummaryinsummaries){Console.WriteLine($"{summary.Customer}: Total ={summary.Total}, Items ={string.Join(", ",summary.Items)}");}// 输出:// Alice: Total = 300, Items = Laptop, Phone// Bob: Total = 50, Items = Mouse

适用场景

CountBy
  • 统计频率:统计集合中元素的出现次数(如单词计数、类别统计)。

  • 分组分析:快速生成键值对形式的计数结果,适合数据分析。

  • 替代GroupBy + Count:在需要简单计数时,CountBy更高效。

AggregateBy
  • 分组聚合:对分组数据执行求和、最大值、最小值、平均值等操作。

  • 复杂聚合:如连接字符串、构建复杂对象等。

  • 高性能场景:需要高效处理大集合,避免中间分组对象的开销。

总结

方法用途替代旧写法场景示例
CountBy按键分组计数GroupBy().Select(g => g.Count())商品销量、用户角色人数
AggregateBy按键分组并执行自定义聚合GroupBy().Aggregate()GroupBy().Sum()日志大小总和、字符串拼接

注意事项:

版本要求:
  • CountByAggregateByC# 13(.NET 9)的新特性,需目标框架为.NET 9.0或更高。

  • 在较低版本中,可使用GroupBy + CountAggregate替代,但性能稍差。

性能优势:
  • 两者直接生成键值对,避免GroupBy的中间IGrouping对象,减少内存分配。

  • 对于大集合或高频操作,性能提升显著。

键比较器:
  • 默认使用EqualityComparer<TKey>.Default,适合大多数场景。

  • 对于自定义类型或特殊相等性逻辑,需提供IEqualityComparer<TKey>

不可变性:
  • 返回的IEnumerable<KeyValuePair<TKey, TValue>>是延迟求值的。

  • 如果需要持久化结果,调用ToList()ToDictionary()

错误处理:
  • 如果sourcenull,会抛出ArgumentNullException

  • 如果keySelectorfunc抛出异常,需在调用代码中处理。

与 GroupBy 的选择:
  • 如果需要访问分组中的所有元素,使用GroupBy

  • 如果只需要键和聚合结果(如计数、总和),优先使用CountByAggregateBy

http://www.jsqmd.com/news/76073/

相关文章:

  • AH807HV:输入200V,输出1.25-30V,2A降压DCDC转换器
  • 全新桌面端酷安体验:告别安卓模拟器的高效解决方案
  • 2025 年广州服装批发市场推荐:原创与效率双驱采批标杆 - 速递信息
  • 滑动验证服务商如何选?2025年年终最新技术评测与权威推荐指南! - 十大品牌推荐
  • 人事外包公司TOP5推荐,高效管理企业人才秘籍 - 速递信息
  • 2025年靠谱的宠物粮专业认证榜 - 品牌宣传支持者
  • 2025年高中电竞学校录取条件解析:王者荣耀职业电竞学校哪家 - 工业推荐榜
  • 3步搞定ggplot2:R语言数据可视化的入门捷径
  • 高品质环保艺术涂料十大品牌榜:用专业与严苛定义墙面美学新时代 - 速递信息
  • Three.js虚拟现实开发完整指南:性能优化与开发效率提升
  • 2025年十大磁悬浮冰水机品牌排行榜,新测评精选磁悬浮冷水机 - mypinpai
  • 2025年热门的对接式垃圾车最新TOP厂家排名 - 行业平台推荐
  • 详细介绍:社区互助|社区​交易|基于springboot+vue的社区​互助交易系统(源码+数据库+文档)
  • CrystalDiskInfo 完整使用教程:轻松监控硬盘健康状况
  • 2025靠谱的全域外卖服务加盟公司TOP5推荐:看哪家口碑好 - myqiye
  • 2025 年 12 月沈阳特产老字号权威推荐榜:地道风味与新春佳礼的匠心之选 - 品牌企业推荐师(官方)
  • 2025年比较好的节能加热圈工厂 - 行业平台推荐
  • 微信编辑器哪个简单好用?3分钟帮你选定,新手必看 - 速递信息
  • 2025 年 12 月电线厂家权威推荐榜:涵盖铜芯/无氧铜/光伏/工业/BVR等全品类,甄选导电强韧与安全耐用的实力品牌 - 品牌企业推荐师(官方)
  • 2025年5款高效薪酬管理系统推荐:优化薪资核算,提升员工满意度与企业效率 - 速递信息
  • 2025年湖南到利雅得空运订舱五大服务商排行榜 - 工业品牌热点
  • RUIE水下图像数据集备用下载指南
  • 2025年公众号排版工具Top5终极评测:谁才是新媒体人的效率神器? - 速递信息
  • 2025高品质/有实力的/知名的纳米粒度及电位分析仪生产厂家|口碑好|质量好的|信用好的|用户好评及推荐 - 品牌推荐大师1
  • 2025年五大兰州口碑好资质齐全美陈设计制作专业公司排行榜, - 工业推荐榜
  • 2025年质量好的果汁浓缩设备厂家最新权威实力榜 - 品牌宣传支持者
  • 2025年口碑好的轻奢开放式衣帽间收纳热门热推榜 - 行业平台推荐
  • 2025年本地检定器行业领军企业实力排行,回弹仪检定器/填土密实度检测仪/一体式楼板测厚仪/混凝土回弹仪/钢砧检定器厂家找哪家 - 品牌推荐师
  • Universal Android Debloater 终极指南:快速清理手机预装应用
  • 防火涂料十大品牌权威排名,守护家居安全的首选推荐 - 速递信息