为什么在实现CompareTo时我必须重载运算符?

假设我有一个实现IComparable的类型。

我原本以为通过调用CompareTo来预期运算符==!=><>=<=将“自动工作”是合理的,但如果我想使用它们,我必须全部覆盖它们。

从语言设计的角度来看,有这样一个很好的理由吗? 是否有任何情况下,当A>BCompare(A,B)>0表现不同时,真的有用吗?

整个局势令人烦恼。 C#有太多表达平等和不平等的方式:

  • ==!=> <> = <=运算符(逻辑上是静态方法)
  • Equals静态方法(调用虚方法),Equals虚方法,ReferenceEquals方法
  • IComparable和IEquatable接口

它们都有微妙的不同语义,除了静态Equals之外,没有一个自动使用另一个,并且没有一个实际上具有我想要的行为。 根据两个操作数的编译时类型调度静态方法; 根据其中一个操作数的运行时类型调度虚方法/接口方法,使操作不对称; 一方的类型比另一方的类型更重要。

我无法想象有人会认为我们所处的情况很好; 没有任何约束,这不是可能发展的。 但是托管语言设计者确实有约束:CLR不在接口契约或双虚拟调度中实现静态方法,或者在generics类型参数上设置运算符约束的能力。 因此,已经发展出多种解决方案来解决平等/不平等问题。

我认为CLR和C#设计师要及时回顾并告诉过去自己CLR的v1中应该有哪些function,接口中的某种forms的静态方法会在列表中占据很高的位置。 如果接口中有静态方法,那么我们可以定义:

 interface IComparable { static bool operator <(T t, U u); static bool operator >(T t, U u); ... etc 

如果你有:

 static void Sort(T[] array) where T : IComparable 

然后,您可以使用<==等运算符来比较元素。

两个主要原因:

  1. 它是所有运营商的一般结构。 虽然比较运算符可能永远不会有替代语义,但在结构中有很大的实用性,它允许某些其他运算符具有非常不同的语义。 仅为比较运算符实现单独的结构将需要省略一些其他可能更有用的function。 例如,在C#中查看这个优雅的BNF实现 。
  2. 对于具有它的值类型的情况,默认实现依赖于Reflection,因此非常低效。 只有您真正知道为类实现这些运算符的最有效方法。 在许多情况下,不需要将结构的所有字段与测试相等进行比较,也不需要将所有字段组合在合适的GetHashCode实现中。 没有默认实现可以确定所有类型,因为它可以简化为停止问题。

根据Eric Lippert等进行更新 ,以下是针对UDT类型的C#中比较运算符的相应标准实现:

 public int CompareTo(UDT x) { return CompareTo(this, x); } public bool Equals(UDT x) { return CompareTo(this, x) == 0; } public static bool operator < (UDT x, UDT y) { return CompareTo(x, y) < 0; } public static bool operator > (UDT x, UDT y) { return CompareTo(x, y) > 0; } public static bool operator <= (UDT x, UDT y) { return CompareTo(x, y) <= 0; } public static bool operator >= (UDT x, UDT y) { return CompareTo(x, y) >= 0; } public static bool operator == (UDT x, UDT y) { return CompareTo(x, y) == 0; } public static bool operator != (UDT x, UDT y) { return CompareTo(x, y) != 0; } public override bool Equals(object obj) { return (obj is UDT) && (CompareTo(this, (UDT)obj) == 0); } 

只需为private static int CompareTo(UDT x, UDT y)添加自定义定义并搅拌即可。