1

考虑以下基本类布局:

public class Base : IComparable<Base>
{
  public int CompareTo(Base other)
  {
    //Do comparison
  }
}

public class Derived : Base, IComparable<Derived>
{
  public int CompareTo(Derived other)
  {
    //Do comparison
  }  
}

public class BaseComparer : IComparer<Base>
{
  public int Compare(Base x, Base y)
  {
   return x.CompareTo(y);
  }
}

然后使用如下:

List<Base> thingies = new List<Base>
{
  new Base(),
  new Derived(),
  new Derived()
};

thingies.Sort(new BaseComparer());

我期望比较器在 x 和 y 参数都是 Derived 实例的情况下调用 Derived.CompareTo 方法。

但是,情况并非如此,而是调用了 Base.CompareTo ,我一直想知道为什么。根据我对 C# 语言规范中描述的重载解决规则的基本理解,我似乎无法推断出这种行为。

有人可以为我解释一下吗?

4

3 回答 3

5

Base对它的派生类一无所知——所以Base只有一种 CompareTo方法,它被无条件地调用。

关键是重载决议发生在编译时没有关于Base可用引用的实际类型的信息。您需要覆盖中的方法Derived,而不是重载它:

public class Derived : Base
{
  public override int CompareTo(Base other)
  {
    //Do comparison
  }  
}

并另外标注Base.CompareTo方法virtual

请注意,这不再实现IComparable<Derived>。您也可以这样做,但与您的目的无关。

于 2010-08-05T09:01:21.223 回答
1

重载解决方案不是这里发生的事情。你有两个独立的方法:它们的全名是IComparable<Base>.CompareToand IComparable<Derived>.CompareTo

唯一BaseComparer知道如何调用的是IComparable<Base>.CompareTo。它一无所知IComparable<Derived>

Base在您的应用程序中,将 a与 a进行比较是否有意义Derived——也就是说,可以说 aBase出现在 a 之前或之后Derived

  • 如果是这样,你最好留在 only IComparable<Base>,甚至是 non-generic IComparable,并准备检查子类中的类型
  • 如果没有,您应该考虑制作Base抽象,并且只IComparable<T>在叶子类上实现
于 2010-08-05T09:05:51.170 回答
1

IComparable<Base>IComparable<Derived>是两种不同的类型,因此其中CompareTo的两个方法Derived映射到两个不同的插槽。CompareToBaseComparer调用方法调用IComparable<Base>。您可以CompareTo(Base)Baseas中表示virtual并覆盖它Derived以获得(部分)预期的行为。

public class Base : IComparable<Base>
{
    public virtual int CompareTo(Base other)
    {
        // do comparison
    }
}

public class Derived : Base, IComparable<Derived>
{
    public int CompareTo(Derived other)
    {
        // do comparison
    }

    public override int CompareTo(Base other)
    {
        if (other is Derived)
            return CompareTo((Derived) other);
        return base.CompareTo(other);
    }
}
于 2010-08-05T09:06:46.427 回答