Разрешение «самого производного» метода в виртуальном переопределении - PullRequest
0 голосов
/ 25 марта 2009

У меня есть простой базовый класс и производный класс:

class Base
{
    public virtual void Write(int value)
    {
        Console.WriteLine("base int: {0}", value);
    }

    public virtual void Write(string value)
    {
        Console.WriteLine("base string: {0}", value);
    }
}

class Derived : Base
{
    public override void Write(int value)
    {
        Console.WriteLine("derived int: {0}", value);
    }

    public virtual void Write(IEnumerable enumerable)
    {
        Console.WriteLine("derived IEnumerable: {0}", enumerable);
    }

    public virtual void Write(object o)
    {
        Console.WriteLine("derived obj: {0}", o);
    }
}

Если я запускаю это:

    static void Main(string[] args)
    {
        Derived d = new Derived();
        Console.WriteLine("derived:");
        d.Write(42);
        d.Write("hello");

        Console.WriteLine("base:");
        Base b = d;
        b.Write(42);
        b.Write("hello");
    }

Я получаю:

derived:
derived obj: 42
derived IEnumerable: hello
base:
derived int: 42
base string: hello

Но я ожидаю, что "b.Write (42)" и "d.Write (42)" будут идентичны. То же самое для строкового регистра.

Что я не понимаю? Как я могу получить поведение, которое я ожидаю, учитывая ограничение, что я не могу изменить «База»?

ОБНОВЛЕНИЕ : См. сообщение Эрика .

Ответы [ 2 ]

3 голосов
/ 25 марта 2009

Это происходит потому, что C # рассматривает методы, объявленные в типе, прежде всего, включая методы переопределения. См .: Раздел 7.3 спецификации C # .

Это сообщение в блоге объясняет это довольно хорошо, а также объясняет причины.

Такое крайне неинтуитивное поведение оправдывается следующими двумя правилами:

  1. Является ли метод переопределенным или нет, это деталь реализации что должно быть позволено изменить без нарушения кода клиента.
  2. Изменения в базовом классе, которые не нарушают наследуемый класс, должны не ломать клиентов по наследству класс.
1 голос
/ 25 марта 2009

строка может быть неявно приведена к IEnumerable (из символов), но ее ToString () по-прежнему возвращает строку. Следовательно, b.Write("hello"); разрешает виртуальный метод IEnumerable, поскольку он ближе к указанному типу.

Я проверю, но если вы переопределите перегрузку строки в своем производном классе, это может правильно разрешиться в клиентском коде.

EDIT

Я был неправ, переопределение не помогает. Возможно, вам придется переименовать ваши производные виртуалы, чтобы избежать столкновения.

РЕДАКТИРОВАТЬ II

Нижеследующее работает, но это хабер, и мне это не нравится. Добавьте этот метод к Derived:

public new void Write(string value)
{
    base.Write(value);
}
...