如果您指定的类型已经实现Comparer<T>
,IComparable
类的用途是什么?
如果我指定Comparer.Default,并且Customer已经实现了IComparable,那么为什么我会使用Comparer类?
答案 0 :(得分:22)
因为你有时需要保持集合/有序队列按其他东西排序,然后是“自然”顺序或更多,然后存在一个自然顺序。
例如,如果您有平面线,您可能希望按以下方式对其进行排序:
计算机中的任务可以通过以下方式安排:
因此,即使在一个应用程序中,您也可能需要按不同的属性对对象进行排序。您不能通过int compareTo(Object)
方法执行此操作,因为它不能在上下文之间进行区分。但是,您可以添加上下文,即实现CompareByPriority
。
答案 1 :(得分:18)
我认为你的问题是为什么一个基类似乎只有一个有用的方法,如果你直接实现了接口,它恰好与你实现的方法相同。如果我理解正确,我猜你是正确的,从Comparer<T>
派生而不是直接实现IComparer<T>
没有太大的好处,除了基类为你提供了一个通用的非泛型实现一个被覆盖的方法。
但如果你的问题是为什么同时拥有IComparer<T>
和IComparable<T>
,那么
正如其他人所指出的那样,Comparer<T>
允许您定义执行比较的不同方法。它是Strategy design pattern的实现。一个很好的例子是各种StringComparer
属性,例如StringComparer.Ordinal,StringComparer.OrdinalIgnoreCase等。这允许您在IComparable<T>
接口根本无法预料的不同情况下对字符串进行不同的排序。 / p>
但除了能够重新定义执行比较的方式之外,有时提供外部比较器是唯一可行的方法。例如,Windows窗体ListView类允许您为其排序逻辑指定IComparer。但ListViewItem不实现IComparable。因此,如果没有比较器策略知道如何操作,ListViewItem不可排序,因为它没有默认的IComparable实现。
所以在一天结束时,它只是另一个可扩展点,当你不是要排序的类型的作者(或者你需要多个排序策略)时,它允许你更灵活。)
<小时/> 编辑:回应你的评论如下:“如果我有一个实现IComparer(我自己的类)的类,这将允许我对任意数量的属性(自定义排序)进行排序,为什么我会打扰使用Comparer.Default“
也许一个例子会有所帮助。假设您正在编写一个扩展方法,用于检查给定值是否在范围之间。
public static bool Between<T>(this T value, T minValue, T maxValue) {
var comparer = Comparer<T>.Default;
int c1 = comparer.Compare(value, minValue);
int c2 = comparer.Compare(value, maxValue);
return (c1 >= 0 && c2 <= 0);
}
在这种情况下,我对类型T一无所知。它可以实现IComparable
或者它可以实现IComparable<T>
,或者它可以不实现,并且将抛出异常。这也允许我轻松地为这个方法添加一个重载,让调用者在他们自己的比较器中传递。但Comparer在这里派上用场,因为它让我得到一个未知类型的默认比较器,它可能会也可能不会实现通用或非通用的IComparable接口。
答案 2 :(得分:9)
该类型不需要实现IComparable,它可以是任何类型 - T
上没有约束:
public abstract class Comparer<T> : IComparer, IComparer<T>
您创建的新Comparer
实现IComparer<T>
和非通用IComparer
,可用于集合的比较和排序。
您是对的:如果您的类型Customer
实施IComparable
,并且您不需要进行其他比较,则Comparer
对您无用。 .net框架中的大多数类都可以接受IComparable<T>
或Comparer<T>
,因此您可以使用其中任何一个。
但是,你总是这样认为是错误的。可以为非Comparable类型创建Comparer
。请注意,以下是不:
public abstract class Comparer<T> : IComparer, IComparer<T>
where T : IComparable, IComparable<T>
假设您有一个简单的类Person
,并且想要对Persons
的列表进行排序,那么最好用它来编写一个Comparer:
public class Person
{
string Name { get; set; }
}
答案 3 :(得分:8)
这里有一些细微之处:
IComparable<T>
- 它还支持较旧(非通用)IComparable
作为后备。这意味着它不能表达为(例如)通用约束Nullable<T>
,其中T
具有可比性,即使Nullable<T>
明显不是 IComparable
或IComparable<T>
List<T>
可以提供Sort
,即使它不坚持< / em>所有T
都是可排序的;你会惊讶地发现通用约束会以多快的速度累积答案 4 :(得分:2)
public class Person
{
public string LastName;
public string FirstName;
}
public class Class2
{
public void test()
{
List<Person> classList = new List<Person>();
//add some data to the list
PersonComparer comp = new PersonComparer();
classList.Sort(comp);
}
}
public class PersonComparer : Comparer<Person>
{
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;
}
}
答案 5 :(得分:1)
如果某个类型实现了IComparable<T>
,那么使用它几乎肯定比IComparable
更好。对于值类型,IComparable<T>
的性能通常比非泛型IComparable
的性能要好得多。通过允许基于派生类型字段的排名,IComparable<T>
可以使用可继承的引用类型,提供比IComparable
更好的语义。
对于后一个好处的示例,假设一个具有属性ScheduleEvent
的抽象基类EventTime
,它通过排序IComparable<ScheduleEvent>
来实现EventTime
。派生类型包括带有消息字符串的ScheduledPopupMessageEvent
,带有ScheduledGongEvent
参数的GongVolume
。具有相同ScheduleEvent
的多个EventTime
必须为IComparable<ScheduleEvent>.CompareTo
报告零,因为没有安全且一致的方法来排名不同类型的ScheduleEvent
,并且因为两个{{为了保持一致性,两者都报告自己相对于第三个人不必报告,必须将自己报告为相对于彼此不相称。另一方面,ScheduleEvent
实施ScheduledGongEvent
考虑IComparable<ScheduledGongEvent>
以及GongVolume
或EventTime
同样执行此问题也不会有问题它的Message参数。
如果排序例程使用ScheduledPopupMessageEvent
(如果存在),那么它很有用,但如果IComparable<T>
不存在则能够回退到IComparable
。然而,检查类是否实现IComparable<T>
并选择适当的实现是否有点贵。幸运的是,一旦确定某个类型具有IComparable<T>
实现,就可以依赖它来永远拥有一个类型;同样,如果发现某种类型没有这样的实现,它永远不会。此外,如果泛型类具有任何静态字段,则类型参数的每个组合将产生具有不同字段的不同类。因此,第一次使用特定类型参数T运行IComparable<T>
时,它将存储它返回到静态字段的Comparer例程。如果再次使用相同类型运行Comparer<T>.Default
,它将返回相同的比较器例程。虽然使用一个方法创建一个静态Comparer<T>
类似乎很奇怪,但为每个类型Comparer<T>
创建一个单独的Comparer<T>
类提供了一个存储创建的比较例程的位置。
答案 6 :(得分:0)
Comparer<T>
包含实际的比较方法。如果您想要以与IComparable
实现中不同的方式比较对象,则可以使用它。