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

时间:2013-12-15 11:01:31

标签: c# icomparable

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

我原本以为期望运营商==!=><>=<=合理通过调用CompareTo自动“正常工作”,但如果我想使用它们,我必须全部覆盖它们。

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

2 个答案:

答案 0 :(得分:27)

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

  • ==!=&gt; &LT; &gt; =&lt; =运算符(逻辑静态方法)
  • Equals静态方法(调用虚方法),Equals虚方法,ReferenceEquals方法
  • IComparable和IEquatable接口

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

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

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

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

然后如果你有:

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

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

答案 1 :(得分:11)

两个主要原因:

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