2010-11-09 4 views
7

я следующий код, который действительно делает смешной материал:Почему C# вызывает неправильную перегрузку?

class Parent { 
    public virtual void DoSomething(IEnumerable<string> list) { 
     Console.WriteLine("Parent.DoSomething(IEnumerable<string>)"); 
    } 
} 

class Child : Parent { 
    public override void DoSomething(IEnumerable<string> list) { 
     Console.WriteLine("Child.DoSomething(IEnumerable<string>)"); 
    } 

    public void DoSomething(IEnumerable<object> list) { 
     Console.WriteLine("Child.DoSomething(IEnumerable<object>)"); 
    } 
} 

Как вы можете видеть, метод DoSomething в Child переопределяется правильно.

Выходной сигнал следующего кода очень неожиданным:

... 
Child c = new Child(); 
var list = new List<string> { "Hello", "World!" }; 
c.DoSomething(list); 
... 

Печать Child.DoSomething(IEnumerable<object>)

В то время как assinging в Parent ссылку на c генерирует правильный вывод:

... 
Parent c = new Child(); 
var list = new List<string> { "Hello", "World!" }; 
c.DoSomething(list); 
... 

Печать Child.DoSomething(IEnumerable<string>)

Почему происходит ?!

+0

Ну, я проверил ваш код в VS2008 с помощью модульного теста. И я получаю «Ребенок».DoSomething (IEnumerable ) 'в результате в обоих. –

+1

@Draco: он должен работать в .NET 4.0/C# 4.0, чтобы этот эффект вступил в силу, так как 'List ' совместим с 'IEnumerable ' с новой поддержкой поддержки изменений. –

+0

Я тоже не смог воспроизвести это поведение. VS2010 .NET 4.0 C# и всегда получил «Child.DoSomething (IEnumerable )» в качестве вывода. Вы должны иметь больше кода, чем это, вызывая это поведение. Пожалуйста, разместите свой полный код, и мы сами увидим. –

ответ

9

Это происходит потому, что C# компилятор подчиняется спецификации :)

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

Теперь, потому что вы используете C# 4 (предположительно) есть неявное преобразование из List<string> в IEnumerable<object> так что ваша перегрузка Child.DoSomething(IEnumerable<object>) применима, и компилятор никогда действительно рассматривает один с помощью IEnumerable<string>.

У меня есть article about overloading, который входит в это и некоторые другие странности.

Советую не перегружать иерархии типов - это сбивает с толку.

+4

Я предполагаю, что для этого есть веские причины, но я думаю, что это неудачная часть спецификации. Это ломается с принципом наименьшего удивления. –

+0

Это указано в [Спецификация C#] (http://download.microsoft.com/download/3/8/8/388e7205-bc10-4226-b2a8-75351c669b09/csharp%20language%20specification.doc), часть 7.5.5.1 - Вызов метода, а также комментарий об этом в разделе 7.4.3 - Разрешение перегрузки: * Например, набор кандидатов для вызова метода не включает переопределенные методы (§7.3) и методы в базовый класс не является кандидатом, если применим какой-либо метод в производном классе (§7.5.5.1). * –

+0

, но как насчет второго, почему во втором он вызывает унаследованный элемент? –

0

soulution Возможное, попробуйте:

class Child : Parent { 
    public override void DoSomething(IEnumerable<string> list) { 
     Console.WriteLine("Child.DoSomething(IEnumerable<string>)"); 
    } 

    public void DoSomething(IEnumerable<object> list) { 
     if(list is IEnumerable<string>){ 
      DoSomething((IEnumerable<string>)list); 
      return; 
     } 
     else 
      Console.WriteLine("Child.DoSomething(IEnumerable<object>)"); 
    } 
} 
0

@Lasse,

Вы писали

Поскольку компилятор использует только тип объекта, который он знает, и он знает, что объект поддерживает то, что находится в Parent, и, таким образом, доступен только один метод при разрешении вызова метода.

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

Что касается метода разрешение правила. Не следует ли выбрать более конкретный метод? Я знаю, что это спорный момент на этом этапе. в 3.5 и 2 он будет выбирать более конкретный метод.

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