Почему C # вызывает неправильную перегрузку? - PullRequest
7 голосов
/ 09 ноября 2010

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

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>)

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

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

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

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

Ответы [ 3 ]

9 голосов
/ 09 ноября 2010

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

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

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

У меня есть статья о перегрузке , которая затрагивает эту и некоторые другие странности.

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

0 голосов
/ 09 ноября 2010

@ Lasse,

вы написали

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

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

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

0 голосов
/ 09 ноября 2010

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

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>)");
    }
}
...