C# list集合 多属性排序

目录

[方法一:使用 LINQ(OrderBy / ThenBy)](#方法一:使用 LINQ(OrderBy / ThenBy))

示例:先按年龄升序,再按分数降序,最后按姓名升序

[方法二:使用 List.Sort + Lambda 表达式](#方法二:使用 List.Sort + Lambda 表达式)

示例:实现同样的多条件排序

[方法三:使用 IComparer 接口(适合复杂/复用逻辑)](#方法三:使用 IComparer 接口(适合复杂/复用逻辑))

[方法四:使用 ComparisonChain 风格(第三方库或自定义辅助类)](#方法四:使用 ComparisonChain 风格(第三方库或自定义辅助类))

总结与建议


如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。

在 C# 中,对 List<T> 进行多属性(多条件)排序主要有两种主流方式:‌LINQ 链式调用 ‌(推荐,代码可读性高)和 ‌**List.Sort 自定义比较器**‌(性能更高,原地排序)。

假设我们有以下数据模型:

public class Student

{

public string Name { get; set; }

public int Age { get; set; }

public double Score { get; set; }

}

var students = new List<Student>

{

new Student { Name = "Alice", Age = 20, Score = 85.5 },

new Student { Name = "Bob", Age = 20, Score = 90.0 },

new Student { Name = "Charlie", Age = 19, Score = 85.5 },

new Student { Name = "David", Age = 20, Score = 85.5 }

};

方法一:使用 LINQ(OrderBy / ThenBy)

这是最直观、易读的方式。它不会修改原列表,而是返回一个新的排序后的列表。

  • OrderBy / OrderByDescending:用于‌第一个‌排序条件。
  • ThenBy / ThenByDescending:用于‌后续‌的排序条件。
示例:先按年龄升序,再按分数降序,最后按姓名升序

using System.Linq;

// 1. 年龄升序 (Age ASC)

// 2. 如果年龄相同,按分数降序 (Score DESC)

// 3. 如果分数也相同,按姓名升序 (Name ASC)

var sortedStudents = students

.OrderBy(s => s.Age) // 第一条件:年龄升序

.ThenByDescending(s => s.Score)// 第二条件:分数降序

.ThenBy(s => s.Name) // 第三条件:姓名升序

.ToList(); // 注意:必须调用 ToList() 才执行排序并生成新列表

foreach (var s in sortedStudents)

{

Console.WriteLine($"Name: {s.Name}, Age: {s.Age}, Score: {s.Score}");

}

优点‌:

  • 代码清晰,逻辑一目了然。
  • 支持任意数量的排序条件。
  • 不修改原始集合。

缺点‌:

  • 会创建新的列表对象,内存开销稍大。
  • 对于超大集合(百万级),性能略低于 Sort

方法二:使用 List.Sort + Lambda 表达式

这种方式是‌原地排序‌(In-place),直接修改原列表,性能通常更好,适合对性能敏感的场景。

示例:实现同样的多条件排序

// 使用 Comparison<T> 委托

students.Sort((x, y) =>

{

// 1. 比较年龄 (升序)

int ageCompare = x.Age.CompareTo(y.Age);

if (ageCompare != 0)

return ageCompare;

// 2. 年龄相同,比较分数 (降序 -> 注意参数顺序交换或取反)

int scoreCompare = y.Score.CompareTo(x.Score); // y.CompareTo(x) 实现降序

if (scoreCompare != 0)

return scoreCompare;

// 3. 分数相同,比较姓名 (升序)

return x.Name.CompareTo(y.Name);

});

关键点‌:

  • CompareTo 返回负数表示前者小,正数表示前者大,0 表示相等。
  • 如果前一个条件不相等(!= 0),直接返回结果;如果相等,继续比较下一个条件。
  • 降序技巧 ‌:使用 y.CompareTo(x) 或者 -x.CompareTo(y)(注意整数溢出风险,推荐前者)。

方法三:使用 IComparer<T> 接口(适合复杂/复用逻辑)

如果排序逻辑非常复杂,或者需要在多处复用,建议实现 IComparer<T> 接口。

public class StudentComparer : IComparer<Student>

{

public int Compare(Student x, Student y)

{

if (x == null && y == null) return 0;

if (x == null) return -1;

if (y == null) return 1;

// 1. 年龄升序

int result = x.Age.CompareTo(y.Age);

if (result != 0) return result;

// 2. 分数降序

result = y.Score.CompareTo(x.Score);

if (result != 0) return result;

// 3. 姓名升序

return string.Compare(x.Name, y.Name, StringComparison.Ordinal);

}

}

// 使用

students.Sort(new StudentComparer());

方法四:使用 ComparisonChain 风格(第三方库或自定义辅助类)

为了简化 Sort 中的 if-else 嵌套,可以编写一个简单的辅助类或使用类似 Google Guava 的链式比较思想。

// 简单的辅助扩展方法

public static class SortExtensions

{

public static void SortBy<T>(this List<T> list, params Func<T, object>\[\] keySelectors)

{

list.Sort((x, y) =>

{

foreach (var selector in keySelectors)

{

var kx = selector(x);

var ky = selector(y);

// 处理 null

if (kx == null && ky == null) continue;

if (kx == null) return -1;

if (ky == null) return 1;

// 比较

int cmp = Comparer<object>.Default.Compare(kx, ky);

if (cmp != 0) return cmp;

}

return 0;

});

}

}

// 使用 (注意:这种方法默认都是升序,且装箱可能有性能损耗,仅适用于简单场景)

// students.SortBy(s => s.Age, s => -s.Score, s => s.Name);

// 注意:-s.Score 仅适用于数值类型且需手动处理降序逻辑,不如 LINQ 直观

总结与建议

特性 LINQ (OrderBy/ThenBy) List.Sort (Lambda) IComparer<T>
可读性 ⭐⭐⭐⭐⭐ (最佳) ⭐⭐⭐ ⭐⭐⭐⭐
性能 中等 (创建新列表) ⭐⭐⭐⭐⭐ (原地排序) ⭐⭐⭐⭐⭐ (原地排序)
是否修改原列表
适用场景 大多数业务场景,Web API,UI 展示 游戏开发,高频循环,大数据量 复杂逻辑,需要复用的排序规则

**推荐做法:**‌

  • 日常开发 ‌:优先使用 ‌LINQ‌,代码维护成本最低。
  • 高性能要求 ‌(如 Unity 游戏每帧排序、大规模数据处理):使用 ‌**List.Sort + Lambda** ‌ 或 ‌**IComparer**‌。

如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。

相关推荐
加号31 小时前
【WPF】 基于 Canvas 读取并渲染 DXF 文件的技术指南
c#·wpf
天下无敌笨笨熊1 小时前
SNMP协议开发心得
网络协议·c#
创可贴治愈心灵2 小时前
AI浪潮下C#就业前景剖析:深耕C#为主,按需选修Java与Python
java·人工智能·c#
专注VB编程开发20年4 小时前
开发VS2026插件最佳方案:老式VSIX EnvDTE
ide·c#·visual studio
专注VB编程开发20年5 小时前
VS2026最新ide插件VisualStudio.Extensibility进程外 OOP 新模型
ide·c#·visual studio
scan7245 小时前
短期记忆记忆存储在内存里,一个会话里的多轮对话
开发语言·c#
JaydenAI5 小时前
[MAF预定义Agent中间件-01]LoggingAgent——在Agent调用前后输出日志
ai·c#·agent·maf·agent管道·agent中中间件
JaydenAI5 小时前
[MAF预定义的AIContextProvider-10]TodoProvider——用TodoList驱动Agent的任务执行
ai·c#·agent·maf
淡水瑜6 小时前
C# 实操
开发语言·c#