如何在C# LINQ中正确使用ThenBy实现自定义排序?
LINQ排序问题:按自定义规则对Person列表排序
定义的Person类与数据源
public class Person { public string FirstName { get; set; } public string LastName { get; set; } public string VisitPlace { get; set; } }
数据源列表:
List<Person> list = new List<Person>() { new Person { FirstName = "Name1", LastName = "Smith", VisitPlace = "London; Paris" }, new Person { FirstName = "Name2", LastName = "Smith", VisitPlace = "Berlin" }, new Person { FirstName = "Name3", LastName = "Smith", VisitPlace = "London; Berlin" }, new Person { FirstName = "Name4", LastName = "Smith", VisitPlace = "Berlin" }, new Person { FirstName = "Name5", LastName = "Smith", VisitPlace = null }, new Person { FirstName = "Name6", LastName = "Smith", VisitPlace = "Paris" }, new Person { FirstName = "Name7", LastName = "Smith", VisitPlace = null }, new Person { FirstName = "Name8", LastName = "Smith", VisitPlace = "Paris; London" }, new Person { FirstName = "Name9", LastName = "Smith", VisitPlace = "London" }, };
排序规则
- 优先排列首访地为London的Person;
- 其次是首访地为Paris的Person;
- 接着是首访地为Berlin的Person(若VisitPlace包含多个地点,仅以第一个为准);
- 所有VisitPlace为null的Person排在最后。
原代码失效原因
你写的LINQ代码:
var list2 = list.OrderByDescending(x => x.VisitPlace).ThenByDescending(x => x.VisitPlace == "London").ThenBy(x => x.VisitPlace == "Paris").ThenBy(x => x.VisitPlace == "Berlin");
失效核心原因有两点:
- 第一步排序覆盖后续逻辑:
OrderByDescending(x => x.VisitPlace)是按字符串字典序降序排列,这一步已经把所有非null元素按字符串本身顺序排好,后续ThenBy仅会在第一步排序后的同一组内生效,但第一步排序后的分组完全不符合规则,导致后续条件无作用空间。 - 首访地判断错误:
x.VisitPlace == "London"仅匹配VisitPlace严格等于"London"的元素(比如Name9),无法匹配"London; Paris"、"London; Berlin"这类多地点元素(Name1、Name3),导致符合规则的元素无法被优先排序。
正确的LINQ实现
要满足排序规则,核心是提取每个Person的首访地,给不同首访地分配明确的优先级权重,按权重降序排列即可:
var sortedList = list.OrderByDescending(p => { // null直接返回最低优先级 if (p.VisitPlace == null) return 0; // 提取第一个访问地点(去除前后空格) var firstVisitPlace = p.VisitPlace.Split(';')[0].Trim(); // 按规则分配优先级数值 return firstVisitPlace switch { "London" => 3, "Paris" => 2, "Berlin" => 1, _ => 0 // 未知地点归为最低优先级 }; }).ToList();
验证结果
排序后顺序为:Name1 → Name3 → Name9 → Name6 → Name8 → Name2 → Name4 → Name5 → Name7,完全符合要求的规则。
逻辑说明
- 通过
Split(';')[0].Trim()准确提取首访地,解决了多地点字符串的判断问题; - 用数值3/2/1/0明确划分优先级,排序时直接按数值降序,严格遵循规则顺序;
- null和未知地点统一返回0,确保排在最后。
内容的提问来源于stack exchange,提问作者Amatour
相关产品推荐
相关产品推荐

