反转比较器装饰器:如何避免名称比较被二次反转
解决方案
要实现主排序(类型)反转、次排序(名称)保持原方向的需求,核心是避免将整个组合比较逻辑一次性反转,而是拆分主/次比较器,仅反转主比较逻辑后再与原次比较器重新组合。
1. 重构比较器结构(推荐,提升复用性)
首先添加ThenBy扩展方法,用于组合主、次比较器,让排序逻辑更模块化:
public static class ComparerExtensions { // 组合主、次比较器:主比较结果不为0时用主结果,否则用次比较结果 public static IComparer<T> ThenBy<T>(this IComparer<T> primary, IComparer<T> secondary) { if (primary == null) throw new ArgumentNullException(nameof(primary)); if (secondary == null) throw new ArgumentNullException(nameof(secondary)); return Comparer<T>.Create((x, y) => { int primaryResult = primary.Compare(x, y); return primaryResult != 0 ? primaryResult : secondary.Compare(x, y); }); } // 仅反转当前比较器的逻辑(不影响后续组合的比较器) public static IComparer<T> Reverse<T>(this IComparer<T> other) { if (other == null) throw new ArgumentNullException(nameof(other)); return other is ReverseComparer<T> reverse ? reverse.Other : new ReverseComparer<T>(other); } public class ReverseComparer<T> : IComparer<T> { public ReverseComparer(IComparer<T> other) { Other = other ?? throw new ArgumentNullException(nameof(other)); } public IComparer<T> Other { get; } public int Compare(T? x, T? y) { return Other.Compare(y, x); } } }
然后拆分原有的MyComparison,将类型、名称比较逻辑分离:
public static class MyComparison { // 仅比较类型的基础比较器(升序) public static readonly IComparer<MyModel> TypeOnly = Comparer<MyModel>.Create((l, r) => l.Type.CompareTo(r.Type)); // 仅比较名称的基础比较器(升序) public static readonly IComparer<MyModel> Name = Comparer<MyModel>.Create((l, r) => l.Name.CompareTo(r.Name)); // 组合逻辑:先类型升序,再名称升序(与原逻辑一致) public static readonly IComparer<MyModel> TypeThenName = TypeOnly.ThenBy(Name); }
现在只需一行代码就能实现类型降序、名称升序的需求:
// 反转类型比较器,再与原名称比较器组合 var typeDescNameAsc = MyComparison.TypeOnly.Reverse().ThenBy(MyComparison.Name);
2. 直接创建目标比较器(快速实现)
如果不想重构现有代码,也可以直接编写目标逻辑,跳过扩展方法:
var typeDescNameAsc = Comparer<MyModel>.Create((l, r) => { // 类型降序:用r.Type和l.Type比较 int typeResult = r.Type.CompareTo(l.Type); if (typeResult != 0) return typeResult; // 名称保持升序:用原逻辑比较 return l.Name.CompareTo(r.Name); });
问题根源说明
原反转逻辑会同时反转名称排序,是因为MyComparison.Type是一个包含了类型+名称的整体比较器,调用Reverse时会将整个比较逻辑反转(包括类型和名称的比较顺序)。通过拆分主/次比较器,我们仅反转主排序逻辑,再与原次排序逻辑组合,就完美规避了这个问题。
内容的提问来源于stack exchange,提问作者Taunter exd
相关产品推荐
相关产品推荐

