Contents
  1. 1. IComparable<T>
  2. 2. IEquatable<T>
  3. 3. IComparer<T>
  4. 4. IEqualityComparer<T>
  5. 5. IComparable<T> VS IComparer<T>

IComparable<T>

命名空间:System

描述
定义值类型或类实现的泛型比较方法,以创建特定类型的比较方法,来实现对其实例进行排序。
此接口由需要对其值进行排序的类型实现,并为需要排序的泛型集合对象的成员提供了强类型比较方法。例如,一个数字可以大于第二个数字,一个字符串可以按字母顺序出现在另一个字符串之前。它要求其实现类型定义唯一的 CompareTo(T) 方法,该方法指示排序顺序中当前实例的位置是在同一类型的第二个对象之前,之后还是相同的位置。通常,不直接在开发人员的代码中调用该方法。相反,它由 List<T>.Sort()Add 等方法自动调用。
通常,提供 Comparable<T> 实现的类型也实现 IEquatable<T> 接口。 IEquatable<T> 接口定义的 Equals 方法,该方法确定实现类型的实例的相等性。
CompareTo(T) 方法的实现必须返回 Int32 值,小于 0 的值表示此对象位于 CompareTo(T) 方法指定的对象之前。大于 0 表示此对象位于 CompareTo(T) 方法指定的对象之后。等于 0 表示此对象和 CompareTo(T) 方法指定的对象位于相同的位置。
所有数字类型(如 Int32Double)都实现 IComparable<T>StringCharDateTime 也是如此。自定义类型也应提供自己的 IComparable<T> 实现,以便对对象实例进行排序。
如果实现了 IComparable<T> 也应该重写 >>=<<= 运算符,来保证与 IComparable<T> 方法返回一致的值。除此之外也应该实现 IEquatable<T> 接口。

IEquatable<T>

命名空间:System

描述
定义值类型或类实现的泛型方法,以创建特定类型的方法来确定实例的相等性。
这个接口被值可以相等的类型实现。(例如,numeric 和 string 类)值类型或类实现 Equals 方法,来创建适用于确定实例相等性的特定类型的方法。
IComparable <T> 接口定义 CompareTo 方法,该方法确定其实现类型实例的排序顺序。
IEquatable <T> 接口定义 Equals 方法,该方法确定其实现类型的实例的相等性。
IEquatable <T> 接口用于泛型集合对象(如, Dictionary<TKey,TValue>List<T>LinkedList<T>),在这些类型的方法中来测试相等,这些方法包括 ContainsIndexOfLastIndexOfRemoveIEquatable <T> 被需要存储在泛型集合中的任意类型实现。
如果实现 IEquatable <T> 方法,也需要重写 Equals(Object)GetHashCode() 的基类实现,以至于它们的行为与 Equals(T) 方法的行为一致。如果重写了 Equals(Object),则在对类的静态Equals(System.Object,System.Object) 方法的调用中也会调用重写的实现。此外,也应该重写 ==!= 运算符。这可确保所有相等测试都返回一致的结果。
对于一个值类型,为了获得更好的性能,都要实现 IEquatable<T> 接口并重写Equals(Object) 方法。Equals(Object) 装箱值类型,并依赖于反射来比较两个值的相等性。实现该接口的类型,其Equals(T) 实现和 Equals(Object) 重写应该返回一致的结果。如果实现 IEquatable<T> 接口,也有对类型的实例进行排序的需求,则还应实现 IComparable<T> 接口。同样的如果实现了 IComparable<T> 接口,也应实现 IEquatable<T> 接口。
请注意,有些设计中的类型支持排序关系,但是相等可能与排序关系不同。想象一下,一个 Person 类,按照字母顺序排序,两个同名的人排序相同,但他们可能不是同一个人。

IComparer<T>

命名空间:System.Collections.Generic

描述
定义类型实现比较两个对象的方法。
此接口与 List <T> .SortList <T> .BinarySearch 方法一起使用。它提供了一种自定义集合实现排序的方法。实现此接口的类包括SortedDictionary <TKey,TValue>SortedList <TKey,TValue> 等泛型类。该接口支持排序比较。也就是说,当 Compare 方法返回 0 时,表示两个对象的排列顺序相同。IEqualityComparer <T> 泛型接口提供了精确的相等比较的实现。
建议从 Comparer <T> 类派生,而不是实现 IComparer <T> 接口,因为 Comparer <T> 类提供 IComparer.Compare 方法的显式接口实现。并且 Comparer <T>Default 属性用于获取对象的默认比较器。

IEqualityComparer<T>

命名空间:System.Collections.Generic

描述:
定义一个方法,用于支持对象之间的比较,判断对象是否相等。
该接口允许为集合实现自定义的相等性比较。也就是说,你可以为类型 T 创建你自己的相等定义;并且在与接受 IEqualityComparer<T> 接口的集合类型一起使用时指定这个相对定义。例如,泛型集合Dictionary<TKey,TValue> 的构造函数就接受该接口。
这个接口只支持相等比较。定制比较与排序的比较有泛型接口 IComparer<T> 提供。
建议从 EqualityComparer<T> 类派生,而不是实现 IEqualityComparer<T> 接口。因为 EqualityComparer <T> 类使用IEquatable <T> .Equals 方法而不是 Object.Equals 方法测试相等性。这与 Dictionary<TKey,TValue> 和其他泛型集合的 ContainsIndexOfLastIndexOfRemove 方法一致。

IComparable<T> VS IComparer<T>

实现了 IComparer<T> 接口的目的,是为了比较类型 T的两个不同对象,实现了IComparable<T> 接口的目的,是为了能够使类型 T 将自己(当前实例)与同类型的其他实例进行比较。
当需要知道另一个实例如何与 this 实例相关时,应该使用 IComparable <T>
IComparer<T> 对于排序集合非常有用,因为 IComparer<T> 位于比较之外。

IComparable,表示我是可以比较的。这意味着我可以与其他东西进行比较。IComparer,我是个比较器,我只是比较一下,这意味着我比较一些东西。
示例:
实现 IComparable<T>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
using System;
using System.Collections.Generic;

public class Temperature : IComparable<Temperature>
{
// The underlying temperature value.
protected double m_value = 0.0;
public Temperature(double kelvins)
{
this.Kelvin = kelvins;
}

public double Celsius
{
get
{
return m_value - 273.15;
}
}

public double Kelvin
{
get
{
return m_value;
}
set
{
if (value < 0.0)
{
throw new ArgumentException("Temperature cannot be less than absolute zero.");
}
else
{
m_value = value;
}
}
}

// Implement the generic CompareTo method with the Temperature
// class as the Type parameter.
//
public int CompareTo(Temperature other)
{
// If other is not a valid object reference, this instance is greater.
if (other == null) return 1;

// The temperature comparison depends on the comparison of
// the underlying Double values.
return m_value.CompareTo(other.m_value);
}
}

实现 IComparer<T>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// This class is not demonstrated in the Main method
// and is provided only to show how to implement
// the interface. It is recommended to derive
// from Comparer<T> instead of implementing IComparer<T>.
public class BoxComp : IComparer<Box>
{
// Compares by Height, Length, and Width.
public int Compare(Box x, Box y)
{
if (x.Height.CompareTo(y.Height) != 0)
{
return x.Height.CompareTo(y.Height);
}
else if (x.Length.CompareTo(y.Length) != 0)
{
return x.Length.CompareTo(y.Length);
}
else if (x.Width.CompareTo(y.Width) != 0)
{
return x.Width.CompareTo(y.Width);
}
else
{
return 0;
}
}
}

上面的示例中,实现了接口 IComparable<T>Temperature 类中,是拿传入 CompareTo 的参数 otherTemperature 的内部字段 m_value 进行比较。而实现了接口 IComparer<T>BoxComp 类中,是拿传入 Compare 方法的两个参数 xy ,在它们之间做比较。

参考

引用至《深入理解C#》(第三版)3.3.3 节

IComparer<T>IComparable<T> 用于排序(判断某个值是小于、等于还是大于另一个),而IEqualityComparer<T>IEquatable<T> 通过某种标准来比较两个项的相等性,或查找某个项的散列(通过相等性概念匹配的方式)。
另一种划分方式:IComparer<T>IEqualityComparer<T> 的实例能够比较两个不同的值,而 IComparable<T>IEquatable<T> 的实例则可以比较它们本身和其他值。