Проблема Перегрузки Метода C# С Классом, Производным От Общего Абстрактного Класса

Я работаю над проектом, и у меня есть общий абстрактный тип, который принимает параметр типа, который сам является производным от абстрактного типа. Если вы хотите знать, почему я это делаю, см. этот вопрос.

я столкнулся с интересной проблемой при перегрузке метода в производном классе, который определен в абстрактном классе. Вот пример кода:

public abstract class AbstractConverter<T, U>
    where U : AbstractConvertible
    where T : AbstractConverter<T, U>
{
    public abstract T Convert(U convertible);
}

public class DerivedConvertibleConverter : AbstractConverter<DerivedConvertibleConverter, DerivedConvertible>
{
    public DerivedConvertibleConverter(DerivedConvertible convertible)
    {
        Convert(convertible);
    }

    public override DerivedConvertibleConverter Convert(DerivedConvertible convertible)
    {
        //This will not be called
        System.Console.WriteLine("Called the most derived method");
        return this;
    }

    public DerivedConvertibleConverter Convert(Convertible convertible)
    {
        System.Console.WriteLine("Called the least derived method");
        return this;
    }
}

public abstract class AbstractConvertible {}

public class Convertible : AbstractConvertible {}

public class DerivedConvertible : Convertible {}

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

пытаясь устранить эту проблему, я столкнулся с интересным решением:

public abstract class AbstractConverter<U>
    where U : AbstractConvertible
{
    public abstract AbstractConverter<U> Convert(U convertible);
}

public class DerivedConvertibleConverter : AbstractConverter<DerivedConvertible>
{
    public DerivedConvertibleConverter(DerivedConvertible convertible)
    {
        Convert(convertible);
    }

    public override DerivedConvertibleConverter Convert(DerivedConvertible convertible)
    {
        System.Console.WriteLine("Called the most derived method");
        return this;
    }

    public DerivedConvertibleConverter Convert(Convertible convertible)
    {
        System.Console.WriteLine("Called the least derived method");
        return this;
    }
}

public abstract class AbstractConvertible {}

public class Convertible : AbstractConvertible {}

public class DerivedConvertible : Convertible {}

когда аргумент производного типа удаляется из базового класса, вызывается наиболее производная версия Convert. Я бы не ожидал этой разницы, так как я бы не ожидал, что интерфейс абстрактной версии Convert будет иметь измененный. Однако я, должно быть, ошибаюсь. Может ли кто-нибудь объяснить, почему возникает эта разница? Заранее большое спасибо.

1 ответов


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

многие люди имеют это ожидание. Однако поведение, которое вы наблюдаете, является правильным и преднамеренным.

алгоритм разрешения перегрузки работает следующим образом. Сначала мы составим список всех возможных доступных методов, которыми вы могли бы быть зовущий. методы, которые переопределяют виртуальные методы, считаются методами класса, который их объявил, а не класса, который их переопределил. затем мы отфильтровываем те, где аргументы не могут быть преобразованы в формальные типы параметров. Затем мы отфильтровываем все методы, которые находятся на любом типе меньше производным чем любой тип, который имеет подходящий метод. Затем мы определяем, какой метод лучше другого, если существует еще более одного метода оставил.

в вашем случае существует два возможных применимых метода. Тот, который принимает DerivedConvertible, считается методом базового класса и поэтому не так хорош, как тот, который принимает конвертируемый.

принцип здесь в том, что переопределение виртуального метода-это деталь реализации, подлежащая изменению, а не намек компилятору, что должен быть выбран метод переопределения.

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

для получения более подробной информации об этих проектных решений см. мою статью на эту тему:

http://blogs.msdn.com/b/ericlippert/archive/2007/09/04/future-breaking-changes-part-three.aspx

когда аргумент производного типа удаляется из базового класса, вызывается наиболее производная версия Convert. Я не ожидал этого разница, так как я не ожидал, что интерфейс абстрактной версии Convert изменится

вопрос основан на ложной предпосылке; наиболее производной версией является не называется. Фрагмент программы ошибочен и поэтому не компилируется, и поэтому ни один метод не вызывается; программа не запускается, потому что она не компилируется.