2013-05-23 4 views
11

Так что я играл с C#, чтобы увидеть, если оно соответствует поведению C++ от этого поста: http://herbsutter.com/2013/05/22/gotw-5-solution-overriding-virtual-functions/ , когда я наткнулся на это очень странное поведение:Перегрузки базовый метод в производном классе

public class BaseClass 
{ 
    public virtual void Foo(int i) 
    { 
     Console.WriteLine("Called Foo(int): " + i); 
    } 

    public void Foo(string i) 
    { 
     Console.WriteLine("Called Foo(string): " + i); 
    } 
} 

public class DerivedClass : BaseClass 
{ 
    public void Foo(double i) 
    { 
     Console.WriteLine("Called Foo(double): " + i); 
    } 
} 

public class OverriddenDerivedClass : BaseClass 
{ 
    public override void Foo(int i) 
    { 
     base.Foo(i); 
    } 

    public void Foo(double i) 
    { 
     Console.WriteLine("Called Foo(double): " + i); 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     DerivedClass derived = new DerivedClass(); 
     OverriddenDerivedClass overridedDerived = new OverriddenDerivedClass(); 

     int i = 1; 
     double d = 2.0; 
     string s = "hi"; 

     derived.Foo(i); 
     derived.Foo(d); 
     derived.Foo(s); 

     overridedDerived.Foo(i); 
     overridedDerived.Foo(d); 
     overridedDerived.Foo(s); 
    } 
} 

Выходной

Called Foo(double): 1 
Called Foo(double): 2 
Called Foo(string): hi 
Called Foo(double): 1 
Called Foo(double): 2 
Called Foo(string): hi 

Таким образом, очевидно, что это подразумевает неявно преобразованное int, чтобы удвоить более конкретный Foo (int) из базового класса. Или он скрывает Foo (int) от базового класса? Но тогда: почему не скрывается Foo (строка)? Чувствует себя очень непоследовательно ... Также не имеет значения, переопределяю ли я Foo (int) или нет; результат тот же. Может ли кто-нибудь объяснить, что здесь происходит?

(Да, я знаю, что это плохая практика, перегружать базовые методы в производном классе - Лисков и все - но я до сих пор не было бы ожидать, что Foo (INT) в OverriddenDerivedClass не называется ?!)

+4

это там, прокрутите вниз – ose

+3

. «Да, я знаю, что это плохая практика, перегружать базовые методы в производном классе - Лиск и все» Да что когда? вам разрешено переопределять базовые методы? В любом случае +1, потому что я ожидал бы того же, любопытного для ответа. – bas

+0

Конечно, вы хотели вызвать методы из 'overrideDerived' во второй партии, не так ли? – Rotem

ответ

8

Чтобы объяснить, как это работает для OverriddenDerivedClass Например:

Посмотрите на # спецификации C для членов поиска здесь: http://msdn.microsoft.com/en-us/library/aa691331%28VS.71%29.aspx

это определяет, как выполняется поиск.

В частности, обратите внимание на эту часть:

Во-первых, множество всех доступных (раздел 3.5) членов с именем N объявлен в T и базовые типы (раздел 7.3.1) Т строится. Объявления, содержащие модификатор переопределения, исключаются из набора.

В вашем случае N является Foo(). Из-за Declarations that include an override modifier are excluded from the set тогда override Foo(int i) исключен из набора.

Следовательно, остается только переопределенный Foo(double i), и, следовательно, это тот, который вызывается.

Вот как это работает для примера OverriddenDerivedClass, но это не объяснение примера DerivedClass.

Чтобы объяснить, что, посмотрите на эту часть спецификации:

Далее, члены, которые скрыты другими членами удаляются из набора.

Foo(double i) в DerivedClass скрывается Foo(int i) от базового класса, поэтому он удаляется из набора.

Коварным здесь та часть, которая говорит:

Все методы с той же сигнатурой, что M объявлен в базовом типе S удаляются из набора.

Вы могли бы сказать: «Но подождите!Foo(double i)не имеют ту же сигнатуру, Foo(int i), поэтому она не должна быть удалена из набора!».

Однако, поскольку существует неявное преобразование из междунар в два раза, это является считается, та же подпись, так Foo(int i)является удаляется из набора

+0

'((BaseClass)) .Foo (i)' – Corak

+0

Хорошо, спасибо за это. Но действительно: «... и базовые типы (раздел 7.3.1) T ... подразумевают, что BaseClass :: Foo (int) будет находиться в пределах видимости. Я могу представить, что набор выглядит так: DerivedClass: : Foo (double), BaseClass :: Foo (int). Когда он принимает целочисленный аргумент, он знает, что он может неявно преобразовывать целое число в double, а BaseClass :: Foo (int) не оценивается. Это было бы верно, однако когда нормальный класс с Foo (double) Foo (int) имеет такое же значение: NormalClass: Foo (int), NormalClass: Foo (double). Можем мы это проверить? – Nebula

+0

@Nebula Извините, не слишком уверен, что вы имеете в виду. .. Однако база 'Foo (int)' находится в области видимости, но потому, что она имеет ту же подпись, что и производный класс 'Foo (double)' (потому что существует неявное преобразование из int в double) производный класс 'Foo (double) 'приводит к тому, что базовый класс' Foo (int) 'будет удален из набора. –

Смежные вопросы