开发者

Method resolution in Comparer

consider the following basic class layout:

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

public class Derived : Base, IComparable<De开发者_如何学运维rived>
{
  public int CompareTo(Derived other)
  {
    //Do comparison
  }  
}

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

and then using those as follows:

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

thingies.Sort(new BaseComparer());

I was expecting the Comparer to be calling the Derived.CompareTo method in those situations where both it's x and y parameters are Derived instances.

However, this is not the case and Base.CompareTo is called instead and I keep wondering why. I can't seem to deduct this behaviour with my basic understanding of the overload resolution rules as described in the C# language specification.

Can someone shed some light on this for me?


Base knows nothing of its derived classes – so in Base there’s only one CompareTo method, and that gets called unconditionally.

The point is that overload resolution happens at compile time where there’s no information about the actual type of Base references available. You need to override the method in Derived, not overload it:

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

And additionally mark the Base.CompareTo method virtual.

Notice that this doesn’t implement IComparable<Derived> any more. You can also do this, but for your purpose that’s unrelated.


Overload resolution is not what's happening here. You've got two independent methods: their full names are IComparable<Base>.CompareTo and IComparable<Derived>.CompareTo.

The only one that BaseComparer knows how to call is IComparable<Base>.CompareTo. It knows nothing about IComparable<Derived>.

In your application, does it make sense to compare a Base with a Derived -- that is, can say that a Base comes before or after a Derived?

  • If so, you'd be better of to stay with only IComparable<Base>, or even the non-generic IComparable, and be prepared to check types in subclasses
  • If not, you should consider making Base abstract, and only implementing IComparable<T> on leaf classes


IComparable<Base> and IComparable<Derived> are two different types, so two methods CompareTo in Derived are mapped onto two different slots. CompareTo invoked by BaseComparer calls method of IComparable<Base>. You can denote CompareTo(Base) in Base as virtual and override it in Derived to get (partially) expected behavior.

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);
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜