Различное поведение перегрузки метода в C # - PullRequest
14 голосов
/ 12 мая 2010

Я проходил через C # Brainteasers (http://www.yoda.arachsys.com/csharp/teasers.html)) и столкнулся с одним вопросом: каким должен быть вывод этого кода?

class Base
{
    public virtual void Foo(int x)
    {
        Console.WriteLine ("Base.Foo(int)");
    }
}

class Derived : Base
{
    public override void Foo(int x)
    {
        Console.WriteLine ("Derived.Foo(int)");
    }

    public void Foo(object o)
    {
        Console.WriteLine ("Derived.Foo(object)");
    }
}

class Test
{
    static void Main()
    {
        Derived d = new Derived();
        int i = 10;
        d.Foo(i);  // it prints ("Derived.Foo(object)"
    }
} 

Но если я изменю код на

class Derived 
{
    public void Foo(int x)
    {
        Console.WriteLine("Derived.Foo(int)");
    }

    public void Foo(object o)
    {
        Console.WriteLine("Derived.Foo(object)");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Derived d = new Derived();
        int i = 10;
        d.Foo(i); // prints  Derived.Foo(int)");

        Console.ReadKey();
    }
}

Я хочу узнать, почему выходные данные меняются, когда мы наследуем, а не наследуем; почему перегрузка методов ведет себя по-разному в этих двух случаях?

Ответы [ 4 ]

19 голосов
/ 12 мая 2010

Как я указал на странице ответов :

Derived.Foo (объект) печатается - при выборе перегрузки, если есть какие-либо совместимые методы, объявленные в производном классе, все сигнатуры, объявленные в базовом классе, игнорируются - даже если они переопределены в том же производном классе!

Другими словами, компилятор просматривает методы, которые были недавно объявлены в наиболее производном классе (на основе типа выражения во время компиляции), и определяет, применимы ли они. Если они есть, он использует «лучший» из доступных. Если ни один из них не применим, он пытается использовать базовый класс и так далее. Переопределенный метод не считается объявленным в производном классе.

Подробнее см. В разделах 7.4.3 и 7.5.5.1 спецификации C # 3.

Теперь точно, почему именно так указано - я не знаю. Для меня имеет смысл, что методы, объявленные в производном классе, имеют приоритет над методами, объявленными в базовом классе, иначе вы столкнетесь с проблемой «хрупкого базового класса» - добавление метода в базовый класс может изменить смысл кода с помощью производный класс. Однако, если производный класс переопределяет метод, объявленный в базовом классе, он четко осознает это, поэтому элемент хрупкости не применяется.

1 голос
/ 12 мая 2010

Это вращается вокруг объема. В первой программе void Foo (int i) принадлежит классу Base. Производный класс просто переопределяет свое поведение.

Foo (int i) игнорируется, потому что он «заимствован» и переопределен через наследование от класса Base. если Foo (объект o) не существует, то будет использоваться Foo (int i).

Во второй программе вызывается void Foo (int i), потому что он должным образом принадлежит классу Derived (т.е. он не заимствован и не переопределен посредством наследования) и имеет лучшее соответствие сигнатуры.

0 голосов
/ 12 мая 2010

Реальный вопрос в том, почему разработчики C # считают, что разрешение перегрузки должно игнорировать переопределенные методы в этой ситуации?

Именно такие проблемы делают перегрузку метода одной из моих наименее любимых функций языка.*

0 голосов
/ 12 мая 2010

Это потому, что сигнатуры методов в производном классе сопоставляются в первую очередь перед рассмотрением сигнатур базового класса. Поэтому при попытке:

d.Foo(i);

Он пытается сопоставить сигнатуру метода с вашим текущим классом (не базовым классом). Он находит приемлемое совпадение Foo(object) и больше не учитывает сигнатуры метода базового класса.

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

-Doug

...