什么是Comparer 类?
如果您指定的类型已经实现IComparable
, Comparer
类的用途是什么?
如果我指定Comparer.Default,并且Customer已经实现了IComparable,那么为什么我会使用Comparer类?
我认为你的问题是为什么一个基类似乎只有一个有用的方法,如果你直接实现了接口,它恰好与你实现的方法相同。 如果我理解正确,我猜你是对的,从Comparer
派生并没有太大的好处,而不是直接实现IComparer
,除了基类为你提供一个通用的非generics实现重写方法。
但是,如果您的问题是为什么同时具有IComparer
和IComparable
,那么正如其他人所指出的那样, 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框架中的大多数类都可以接受IComparable
或Comparer
,因此您可以使用其中任何一个。
但是,你总是这样认为是错误的。 可以为非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
显然不是IComparable
或IComparable
- 它通过不要求它们来防止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
报告零,因为没有安全且一致的方法来对不同类型的ScheduleEvent
进行排名,并且因为两个ScheduleEvent
都报告自己相对于第三个必须没有排序为了保持一致性,将自己报告为相对于彼此不相关。 另一方面,让ScheduledGongEvent
实现IComparable
考虑使用GongVolume
以及EventTime
,或使用ScheduledPopupMessageEvent
同样使用其Message参数也没有问题。
那么,排序例程之类的东西使用IComparable
如果它存在)是有用的,但如果IComparable
不存在则能够回退到IComparable
。 检查类是否实现IComparable
并选择适当的实现,但是,它有点贵。 幸运的是,一旦确定某个类型具有IComparable
实现,就可以依赖它来永远拥有一个; 同样,如果发现某种类型没有这样的实现,它永远不会。 此外,如果generics类具有任何静态字段,则类型参数的每个组合将产生具有不同字段的不同类。 因此,第一次使用特定类型参数T运行Comparer
,它将存储它返回到静态字段的Comparer例程。 如果使用相同类型再次运行Comparer
,它将返回相同的比较器例程。 虽然使用一个方法获得静态Comparer
类似乎很奇怪,但为每个类型T
创建一个单独的Comparer
类提供了一个存储创建的比较例程的位置。
Comparer
拥有实际的比较方法。 如果要以与IComparable
实现中不同的方式比较对象,则可以使用它。