什么是Comparer 类?

如果您指定的类型已经实现IComparableComparer类的用途是什么?

如果我指定Comparer.Default,并且Customer已经实现了IComparable,那么为什么我会使用Comparer类?

我认为你的问题是为什么一个基类似乎只有一个有用的方法,如果你直接实现了接口,它恰好与你实现的方法相同。 如果我理解正确,我猜你是对的,从Comparer派生并没有太大的好处,而不是直接实现IComparer ,除了基类为你提供一个通用的非generics实现重写方法。

但是,如果您的问题是为什么同时具有IComparerIComparable ,那么正如其他人所指出的那样, Comparer允许您定义执行比较的不同方法。 它是战略设计模式的实施。 一个很好的例子是各种StringComparer属性,如StringComparer.Ordinal,StringComparer.OrdinalIgnoreCase等。这允许您在IComparable接口无法预料的不同情况下对字符串进行不同的排序。

但除了能够重新定义比较的执行方式之外,有时提供外部比较器是唯一可行的方法。 例如,Windows窗体ListView类允许您为其排序逻辑指定IComparer。 但ListViewItem不实现IComparable。 因此,如果没有比较器策略知道如何操作,ListViewItem不可排序,因为它没有默认的IComparable实现。

因此,在一天结束时,它只是另一个可扩展性点,当您不是要排序的类型的作者(或者您需要多个排序策略)时,它允许您更灵活。


编辑:响应你的评论如下: “如果我有一个实现IComparer(我自己的类)的类,这将允许我排序任意数量的属性(自定义排序),为什么我会打扰使用Comparer.Default “

也许一个例子会有所帮助。 假设您正在编写一个扩展方法,用于检查给定值是否在范围之间。

 public static bool Between(this T value, T minValue, T maxValue) { var comparer = Comparer.Default; int c1 = comparer.Compare(value, minValue); int c2 = comparer.Compare(value, maxValue); return (c1 >= 0 && c2 <= 0); } 

在这种情况下,我对类型T一无所知。它可以实现IComparable ,也可以实现IComparable ,也可以不实现,也会抛出exception。 这也允许我轻松地为这个方法添加一个重载,让调用者在他们自己的比较器中传递。 但Comparer在这里派上用场,因为它让我得到一个未知类型的默认比较器,它可能会也可能不会实现通用或非通用IComparable接口。

因为你有时需要保持集合/有序队列按其他方式排序,然后是“自然”顺序或更多,然后存在一个自然顺序。

例如,如果您有平面线,您可能希望按以下方式对其进行排序:

  • 航class号
  • 目的地
  • 时间
  • 优先级(某些航class可能比其他航class延误更长)

计算机中的任务可以安排:

  • 用户
  • 优先级(在调度程序中)
  • PID(正常比较)

因此,即使在一个应用程序中,您可能需要按不同属性对对象进行排序。 您无法通过int compareTo(Object)方法执行此操作,因为它不能在上下文之间进行区分。 但是,您可以添加上下文,即实现CompareByPriority

该类型不需要实现IComparable,它可以是任何类型 – T上没有约束

 public abstract class Comparer : IComparer, IComparer 

您创建的新Comparer实现IComparer和非通用IComparer ,可用于集合的比较和排序。

你是对的:如果你的类型, Customer实现IComparable ,而你不需要另外的比较, Comparer对你Comparer 。 .net框架中的大多数类都可以接受IComparableComparer ,因此您可以使用其中任何一个。

但是,你总是这样认为是错误的。 可以为非Comparable类型创建Comparer 。 请注意,以下内容不是必需的:

 public abstract class Comparer : IComparer, IComparer where T : IComparable, IComparable 

假设你有一个简单的类, Person ,你想要对Persons列表进行排序,你最好把它写成一个Comparer:

 public class Person { string Name { get; set; } } 

这里有一些微妙的要点:

  • 它不仅支持IComparable – 它还支持较旧(非通用) IComparable作为后备。 这意味着它不能表示为(例如)通用约束
  • 它支持Nullable ,其中T是可比较的,即使Nullable显然不是 IComparableIComparable
  • 它通过要求它们来防止generics类型约束的爆炸 – 例如, List可以提供Sort即使它并不坚持所有T都是可排序的; 你会惊讶于通用约束会以多快的速度积累
  • 它允许您将比较器传递给任何需要比较器的现有API,只要您拥有的是可以比较的类型 ; 大多数框架排序API(包括LINQ)都将提供比较器支持
 public class Person { public string LastName; public string FirstName; } public class Class2 { public void test() { List classList = new List(); //add some data to the list PersonComparer comp = new PersonComparer(); classList.Sort(comp); } } public class PersonComparer : Comparer { public override int Compare(Person x, Person y) { int val = x.LastName.CompareTo(y.LastName); if (val == 0) { val = x.FirstName.CompareTo(y.FirstName); } return val; } } 

如果一个类型实现IComparable ,那么使用它比IComparable几乎肯定更好。 对于值类型, IComparable的性能通常比非通用IComparable的性能要好得多。 对于可inheritance的引用类型, IComparable可以提供比IComparable更好的语义,允许基于派生类型字段的排名。

有关后一个好处的示例,假设有一个抽象基类ScheduleEvent ,其属性为EventTime ,它通过对EventTime进行排序来实现IComparable 。 派生类型包括带有消息字符串的ScheduledGongEvent ,带有GongVolume参数的ScheduledGongEvent 。 具有相同EventTime多个ScheduleEvent必须为IComparable.CompareTo报告零,因为没有安全且一致的方法来对不同类型的ScheduleEvent进行排名,并且因为两个ScheduleEvent都报告自己相对于第三个必须没有排序为了保持一致性,将自己报告为相对于彼此不相关。 另一方面,让ScheduledGongEvent实现IComparable考虑使用GongVolume以及EventTime ,或使用ScheduledPopupMessageEvent同样使用其Message参数也没有问题。

那么,排序例程之类的东西使用IComparable如果它存在)是有用的,但如果IComparable不存在则能够回退到IComparable 。 检查类是否实现IComparable并选择适当的实现,但是,它有点贵。 幸运的是,一旦确定某个类型具有IComparable实现,就可以依赖它来永远拥有一个; 同样,如果发现某种类型没有这样的实现,它永远不会。 此外,如果generics类具有任何静态字段,则类型参数的每个组合将产生具有不同字段的不同类。 因此,第一次使用特定类型参数T运行Comparer.Default ,它将存储它返回到静态字段的Comparer例程。 如果使用相同类型再次运行Comparer ,它将返回相同的比较器例程。 虽然使用一个方法获得静态Comparer类似乎很奇怪,但为每个类型T创建一个单独的Comparer类提供了一个存储创建的比较例程的位置。

Comparer拥有实际的比较方法。 如果要以与IComparable实现中不同的方式比较对象,则可以使用它。