何时使用IComparable<T>与何时使用IComparer<T>

111

我正在尝试弄清楚我需要实现哪个接口,它们基本上都做同样的事情。在什么情况下我会选择使用其中一个而不是另一个?


2
在这里可以找到很好的示例和解释: https://support.microsoft.com/nl-nl/help/320727/how-to-use-the-icomparable-and-icomparer-interfaces-in-visual-c - Jan
8个回答

104

它们并不完全相同,因为 IComparer<T> 实现在能够比较两个不同对象的类型上,而 IComparable<T> 实现在能够将自身与同一类型的其他实例进行比较的类型上。

我倾向于在需要知道另一个实例与 this 实例的关系时使用 IComparable<T>。而对于排序集合,IComparer<T> 在比较之外非常有用。


10
IComparer<T> 还允许您为每种排序类型创建一个类。例如:PersonLastFirstNameComparer、PersonFirstLastNameComparer 或 PersonAgeComparer。在这些类中,您可以实现自定义的排序逻辑。 - user117499
你有什么简单的方法来记住它们吗?我倾向于每次都要查一下。 - amadib
64
@amadib认为IComparable可以理解为“我是可比较的”,这意味着我可以与其他东西进行比较。而IComparer则可以理解为“我是一个比较器,我只是比较”,这意味着我会比较一些东西。 - nawfal
@newfal 你应该把这个作为答案发布。我认为这里是最好的解释。 - Gene S

43

当一个类具有内在比较时,请使用 IComparable<T>

如果想要除了类的内在比较之外的其他比较方法,可以使用IComparer<T>(如果类有内在比较,则也适用)。


30

这取决于实体。例如,对于像“学生”这样的类,基于名称的IComparable是有意义的。

class Student : IComparable 
{
    public string Name { get; set; }
    public int MathScore { get; set; }
    public int EnglishScore { get; set; }

    public int TotalScore 
    {
        get
        {
            return this.MathScore + this.EnglishScore; 
        }
    }

    public int CompareTo(object obj)
    {
        return CompareTo(obj as Student);  
    }

    public int CompareTo(Student other)
    {
        if (other == null)
        {
            return 1;
        }
        return this.Name.CompareTo(other.Name);  
    }
}

但是,如果教师'A'想要根据数学成绩比较学生,而教师'B'想要根据英语成绩比较学生。实现单独的IComparer可能是一个好主意。(更像一种策略模式)

class CompareByMathScore : IComparer<Student>
{
    public int Compare(Student x, Student y)
    {
        if (x.MathScore > y.MathScore)
          return 1;
        if (x.MathScore < y.MathScore)
          return -1;
        else
          return 0;
    }
}

1
将Compare方法设为_static_以便于使用。 - Jan

14

简单解释的故事

高中篮球。这是一个选队的场景。我想让身高/实力/速度最好的人来组成我的队伍。该怎么办?

IComparer接口 - 比较两个不同的人

  • 这使我可以比较任何两个排成一行的人……基本上就是这样。Fred vs John……我把他们放在一个实现了该接口的具体类中。Compare(Fred, John),它会输出谁更好。

IComparable呢? - 把自己和别人比较

最近有没有看到过Facebook上的其他人做酷炫的事情:环游世界、发明创造,而我做的事情可能不那么酷炫——我们正在使用IComparable接口。

  • 我们正在将当前实例(自己)与另一个相同类型的对象(其他人)进行比较(person)。

比较器类怎么样?

Comparator类是一个抽象基类,实现了IComparer接口。你应该从这个类派生出一个具体的实现。总之,微软建议你使用比较器类而不是实现IComparer接口:

我们建议您从Comparer类派生,而不是实现IComparer接口,因为Comparer类提供了IComparer.Compare方法和获取对象的默认比较器的Default属性的显式接口实现。

总结

  • IComparer - 排列两个事物并进行比较。
  • IComparable - 在Facebook上将自己与他人进行比较。

希望这些故事能帮助你记住。


1
我喜欢你阐述关键概念的方式。如果你把Comparer(T)类也包含在这个上下文中会更好,即使它不在问题中 :) - Kevman

10
一切都取决于您的类型是否可变。您应该仅在不可变类型上实现IComparable接口。请注意,如果您实现了IComparable,则必须重写Equals以及==,!=,<和>运算符(请参阅Code Analysis警告CA1036)。
引用来自Dave G的这篇博客文章
“但正确的答案是,如果您的对象是可变的,则应该使用IComparer而不是IComparable,并在必要时将IComparer的实例传递给排序函数。”
由于IComparer仅是用于排序的一次性对象,在那个时间点使用它时,您的对象可以具有任何可变语义。此外,它不需要甚至不建议使用Equals,GetHashCode或==-您可以自由定义任何方式。
最后,您可以为类型定义多个IComparer,以便根据不同的字段或不同的规则进行排序。这比被固定一个定义要灵活得多。
简而言之:对于值类型,请使用IComparable;对于引用类型,请使用IComparer。

4

正如其他人所说,它们的作用不同。

无论如何,现在我不太使用IComparer。为什么呢?它的职责(用于比较两个对象的外部实体)可以更清晰地通过lambda表达式来处理,就像LINQ的大多数方法一样。编写一个快速lambda,将要比较的对象作为参数,并返回一个布尔值。如果对象定义了自己的内在比较操作,则可以实现IComparable。


1
-1:返回bool值与IComparer不等价。 IComparer返回一个可以小于零/等于零/大于零的值,通常用于排序。 - Joe
当你需要这样做时,你可以返回一个int(或更好的是枚举)。这真的很重要吗? - jalf
2
你甚至可以返回一个布尔值,因为小于是排序序列所需的唯一操作。 - jalf
一个IComparer实现只需要定义一次,如果你需要在更多的地方使用排序逻辑,那么lambda表达式就需要写更多次。 - oɔɯǝɹ
oɔɯǝɹ - 然后,您可以将以lambda表达式编写的委托的引用存储起来并重复使用它。 - jpierson

4

IComparer是一个接口,用于对数组进行排序,这个接口将强制实现类去实现Compare(T x,T y)方法,该方法将比较两个对象。实现了这个接口的类的实例将被用于数组的排序。

IComparable是一个接口,它在需要比较同一类型的两个对象的类型中实现。这个可比较的接口将强制实现类去实现以下方法CompareTo(T obj)

IEqualityComparer是一个接口,它用于查找对象是否相等,现在我们将在一个示例中看到这个接口,我们必须在集合中找到对象的不同之处。该接口将实现一个方法Equals(T obj1,T obj2)

现在我们来举个例子,我们有一个Employee类,基于这个类,我们必须创建一个Collection。现在我们有以下要求。

使用Array类对数组进行排序。 2.使用Linq创建一个Collection:删除重复项,按高到低排序,删除一个员工ID。

abstract public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Address { set; get; }
}

public enum SortType
{
    ByID,
    BySalary
}

公共类EmployeeIdSorter : IComparer { public int Compare(Employee x, Employee y) { 如果(x.Id < y.Id) 返回1; else 如果(x.Id > y.Id) 返回-1; else 返回0; } }

这是一个用于比较员工ID的类。如果x的ID小于y的ID,则返回1;如果x的ID大于y的ID,则返回-1;如果两个ID相等,则返回0。
    public class EmployeeSalarySorter : IComparer<Employee>
    {
        public int Compare(Employee x, Employee y)
        {
            if (x.Salary < y.Salary)
                return 1;
            else if (x.Salary > y.Salary)
                return -1;
            else
                return 0;
        }
    }

以下是更多信息,请参见下面链接:http://dotnetvisio.blogspot.in/2015/12/usage-of-icomparer-icomparable-and.html


3

IComparable表示一个对象可以与另一个对象进行比较。 IComparer是一个可以比较任意两个项目的对象。


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