在LINQ中按多列分组:示例与最佳实践

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

如何使用LINQ在C#中按两个或更多字段对数据进行分组?

\n\n

你好,朋友!如果你曾经用SQL编写过数据库查询,你一定记得带有多个列的GROUP BY结构。它允许你将数据拆分成逻辑分组——例如,先按城市分组销售数据,然后在每个城市内按商品类别分组。在C#中,同样的目的可以通过LINQ的GroupBy实现。但问题在于:标准的GroupBy只接受一个键。如果需要按多个列分组该怎么办?

\n\n

别担心!解决方案既优雅又简单——我们创建一个匿名类型,将所需字段合并成一个复合键。在本文中,我不仅会展示基本语法,还会提供实际代码示例,你可以立即应用到自己的项目中。开始吧!

\n\n

问题:一个键太少了

\n\n

假设你有一个学生列表:

\n\n
public class Student\n{\n    public string Name { get; set; }\n    public string Course { get; set; }\n    public int Grade { get; set; }\n}\n\nList<Student> students = new List<Student>\n{\n    new Student { Name = "安娜", Course = "数学", Grade = 85 },\n    new Student { Name = "鲍里斯", Course = "数学", Grade = 90 },\n    new Student { Name = "安娜", Course = "物理", Grade = 78 },\n    new Student { Name = "鲍里斯", Course = "物理", Grade = 82 },\n    new Student { Name = "安娜", Course = "数学", Grade = 95 }\n};
\n\n

你想知道每个学生在每门课程中的平均成绩。如果只按姓名使用GroupBy,安娜的数学和物理数据会混在一起。如果只按课程分组,则会丢失与学生的关联。需要按两列分组:Name + Course。

\n\n

解决方案:匿名类型作为复合键

\n\n

在C#中,我们可以直接在现场创建一个包含所需字段的临时对象:

\n\n
var grouped = students\n    .GroupBy(s => new { s.Name, s.Course })\n    .Select(g => new\n    {\n        g.Key.Name,\n        g.Key.Course,\n        AverageGrade = g.Average(st => st.Grade)\n    });\n\nforeach (var item in grouped)\n{\n    Console.WriteLine($"{item.Name} | {item.Course} | 平均分: {item.AverageGrade:F2}");\n}
\n\n

输出:
\n安娜 | 数学 | 平均分: 90.00
\n鲍里斯 | 数学 | 平均分: 90.00
\n安娜 | 物理 | 平均分: 78.00
\n鲍里斯 | 物理 | 平均分: 82.00

\n\n

请注意:new { s.Name, s.Course } 就是匿名类型。LINQ会自动根据对象的所有属性进行比较(值相等性)。

\n\n

使用IGrouping和嵌套集合

\n\n

有时你不需要聚合数据,而是希望保留组内的所有原始元素。这时可以使用IGrouping<TKey, TElement>接口。每个组包含一个键和一个元素集合:

\n\n
var groups = students.GroupBy(s => new { s.Name, s.Course });\n\nforeach (var group in groups)\n{\n    Console.WriteLine($"学生: {group.Key.Name}, 课程: {group.Key.Course}");\n    foreach (var student in group)\n    {\n        Console.WriteLine($"  成绩: {student.Grade}");\n    }\n}
\n\n

当你需要输出详细报告时,这种方法非常理想。

\n\n

查询语法 vs 流畅语法——如何选择?

\n\n

LINQ支持两种编写查询的风格。让我们看看两者:

\n\n

1. 流畅语法(方法语法)

\n
var result = students\n    .GroupBy(s => new { s.Name, s.Course })\n    .Select(g => new { g.Key.Name, g.Key.Course, Count = g.Count() });
\n\n

2. 查询语法(类似SQL的表达式语法)

\n
var result = from student in students\n             group student by new { student.Name, stud                

博客

书籍推荐