Разрешение метода в Comparer

рассмотрим следующий базовый макет класса:

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());

Я ожидал, что Comparer будет вызывать метод Derived.CompareTo в тех ситуациях, когда оба его параметра x и y являются производными экземплярами.

Однако это не так, и вместо этого вызывается Base.CompareTo, и я продолжаю задаваться вопросом, почему. Я не могу объяснить это поведение своим базовым пониманием правил разрешения перегрузок, как описано в спецификации языка C#.

Может ли кто-нибудь пролить свет на это для меня?


person Anton    schedule 05.08.2010    source источник
comment
Прочтите сообщения в блоге Эрика Липпертса о разрешении перегрузки — blogs.msdn.com/ б/ериклипперт/архив/теги/перегрузка+разрешение   -  person Oded    schedule 05.08.2010


Ответы (3)


Base ничего не знает о своих производных классах, поэтому в Base есть только один метод CompareTo, и он вызывается безоговорочно.

Дело в том, что разрешение перегрузки происходит во время во время компиляции, когда нет информации о фактическом типе доступных Base ссылок. Вам нужно переопределить метод в Derived, а не перегрузить его:

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

И дополнительно отметить Base.CompareTo способ virtual.

Обратите внимание, что это больше не реализует IComparable<Derived>. Вы можете также сделать это, но для ваших целей это не имеет отношения.

person Konrad Rudolph    schedule 05.08.2010
comment
Спасибо за ваши усилия. Я пытался быть умным и предотвратить виртуальную/переопределяющую реализацию в CompareTo(Base), используя перегрузку для конкретного сценария, где и x, и y являются производными экземплярами. Кажется, я не был таким умным в конце концов :) - person Anton; 05.08.2010

Разрешение перегрузки - это не то, что здесь происходит. У вас есть два независимых метода: их полные имена IComparable<Base>.CompareTo и IComparable<Derived>.CompareTo.

Единственный, кого BaseComparer умеет звать, это IComparable<Base>.CompareTo. Он ничего не знает о IComparable<Derived>.

Имеет ли смысл в вашем приложении сравнивать Base с Derived, то есть можно ли сказать, что Base стоит до или после Derived?

  • Если это так, вам лучше остаться только с IComparable<Base> или даже с неуниверсальным IComparable и быть готовым проверять типы в подклассах.
  • Если нет, вам следует подумать о том, чтобы сделать Base абстрактным и реализовать IComparable<T> только в листовых классах.
person Tim Robinson    schedule 05.08.2010
comment
Так что я вижу сейчас. В моем случае имеет смысл сравнивать экземпляры Base и Derived. Я пытался быть умным и должен написать отдельную реализацию CompareTo только в том случае, когда должно иметь место более тонкое сравнение (то есть, когда оба экземпляра являются производными), а для всех других сценариев просто позволяйте разрешению перегрузки вернуться к достаточное сравнение в Base.CompareTo. Увы... :) - person Anton; 05.08.2010

IComparable<Base> и IComparable<Derived> — это два разных типа, поэтому два метода CompareTo в Derived отображаются на два разных слота. CompareTo, вызванный BaseComparer, вызывает метод IComparable<Base>. Вы можете обозначить CompareTo(Base) в Base как 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);
    }
}
person desco    schedule 05.08.2010