IComparable和IComparer的区别

64

IComparableIComparer接口之间有什么区别?在使用Array.Sort()方法时是否总是需要使用这个接口?


4
你是否查阅文档 - Cody Gray
3
一个实现 IComparable 接口的对象,可以在不使用 IComparer 的情况下进行比较。简单明了。;) - jalf
6个回答

108
正如其名称所示,IComparable<T> 读作 我可以比较。当为T定义IComparable<T>时,您可以将当前实例与另一个相同类型的实例进行比较IComparer<T> 读作我是比较器,我比较。通常在T实例之外比较任何两个T实例时使用IComparer<T>
起初可能会困惑它们的用途是什么。从定义中应清楚地了解到,因此IComparable<T>(在类T本身中定义)应成为提供排序逻辑的事实标准。默认的List<T>等上的Sort依赖于此。在T上实现IComparer<T>对于常规排序没有帮助。因此,在除T以外的任何其他类上实现IComparable<T>都没有多少价值。
class MyClass : IComparable<T>

很少有意义。
另一方面,
class T : IComparable<T>
{
    public int CompareTo(T other)
    {
        //....
    }
}

这是应该的做法。
当您需要根据自定义顺序进行排序时,IComparer<T>可能很有用,但并不是普遍规则。例如,在一个Person类中,您可能需要根据年龄对人进行排序。在这种情况下,您可以执行以下操作:
class Person
{
    public int Age;
}

class AgeComparer : IComparer<Person>
{
    public int Compare(Person x, Person y)
    {
        return x.Age - y.Age;
    }
}

现在,AgeComparer有助于根据Age对列表进行排序。
var people = new Person[] { new Person { age = 23 }, new Person(){ age = 22 } };
people.Sort(p, new AgeComparer()); //person with age 22 comes first now.

同样在 T 上使用 IComparer<T> 是没有意义的。

class Person : IComparer<Person>

确实,这样做是可行的,但不美观且违背逻辑。

通常您需要的是 IComparable<T>。理想情况下,您只能拥有一个 IComparable<T>,而可以根据不同的标准拥有多个 IComparer<T>

IComparer<T>IComparable<T> 与用于测试相等性而非比较/排序的 IEqualityComparer<T>IEquatable<T> 完全类似;在这里有一个很好的线程 here,我写了完全相同的答案 :)


3
谢谢您的提问。关于您的问题:"类 MyClass : IComparable<T> 很少有意义。" 为什么呢?它有什么问题吗?这是因为通常应该使用泛型类型参数来实现IComparable接口,而不是在类定义中指定类型参数。这样做可以使代码更加灵活和可重用,并且可以避免类型转换问题。因此,建议将实现IComparable接口的类型参数移到类方法或构造函数中。 - BenKoshy
2
@BKSpurgeon 因为 IComparable<T> 是为了为 T 的实例提供默认顺序。不仅从名称 IComparable 中可以看出这一点,而且从 IComparable 上的 CompareTo 方法的定义中也可以看出这一点。该定义是 public int CompareTo(T),因此您可以执行 jon.CompareTo(mark),其中 jon 和 mark 当然是人名。当您编写 class RandomClass : IComparable<SomethingElse> 时,您的 CompareTo 会使代码看起来像 refrigerator.CompareTo(parrot)。虽然在技术上某些情况下可能需要这样做,但 .NET 世界并不是这样做的。 - nawfal
1
@BKSpurgeon 对于.NET开发人员来说,这可能会令人困惑,因为这不是常规做法。在需要的情况下,您可以在适当的类中拥有一个静态方法(可以是扩展方法),该方法称为public int Compare(Refrigerator r, Parrot p) - nawfal
我认为这样做不会起作用 - people.Sort(p, new AgeComparer()); 应该使用 Array.Sort(people ,new AgeComparer()) - ni3.net

44
  • IComparable - 定义了一个接口,其中包含一个 CompareTo() 方法,该方法接受相同类型的另一个对象并将当前对象与传递的对象进行比较。它内部化了对象之间的比较,允许更加内联的比较操作,并且在只有一种逻辑方式或者压倒性的默认比较方式来比较一个类型的对象时非常有用。
  • IComparer - 定义了一个接口,其中包含一个 Compare() 方法,该方法接受两个不必实现 IComparable 接口的对象并对它们进行比较。这样可以外部化比较操作,并且当有许多可行的方法来比较一个类型的两个对象时使用,或者当该类型没有实现 IComparable 接口(或者 IComparable 实现与你想要的不同)并且你无法控制该类型的源代码时非常有用。

请查看Visual Studio Magazine文章《使用IComparable和IComparer进行多级排序》(https://visualstudiomagazine.com/articles/2011/10/01/multilevel-sorting-with-icomparable-and-icomparer.aspx)以获取详细示例。 - lauxjpn

16

我看到的最好的解释是:“要进行排序的对象将实现IComparable接口,而将对这些对象进行排序的类将实现IComparer接口。” (来源)

如果您尝试排序的对象没有实现IComparable接口,则需要创建一个实现IComparer接口的类(并接受这些对象类型作为比较对象),然后将其传递给Array.Sort()方法。


7

IComparable被用来为你的对象提供默认的排序顺序。

IComparer则提供额外的比较机制。

参考此文章了解更多信息。


3

IComparer比较给定的两个对象。IComparable是由正在比较的对象实现的,目的是与其自身的另一个对象进行比较。

实现IComparable对于排序对象是一个好主意。 IComparable可用于按不同标准排序(例如,按对象中可选择的字段排序)。

另请参见:何时使用IComparable<T> Vs. IComparer<T>


1

实现IComparable接口的类的实例可以与其他对象进行比较。(将IComparable读作“我可以比较”)。执行此操作的接口方法是CompareTo。如果您希望这些类的实例知道如何将自己与其他对象进行比较,则此功能非常有用。

实现IComparer接口的类的实例可用于比较对象对。(将IComparer读作“我可以比较”)。执行此操作的接口方法是Compare。如果您想要将比较逻辑与被比较对象的类分离,那么这个功能非常有用。一个使用场景是当您有多种比较对象的方式时(例如不区分大小写与区分大小写的字符串比较)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接