2016-07-22 10 views
14

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

Например, рассмотрим следующий код:

public interface IBase 
{ 
    string Method(); 
} 

public interface IDerived : IBase 
{ 
    new string Method(); 
} 

public class Foo : IDerived 
{ 
    string IBase.Method() 
    { 
     return "IBase.Method"; 
    } 

    string IDerived.Method() 
    { 
     return "IDerived.Method"; 
    } 
} 

static class Program 
{ 
    static void Main() 
    { 
     IDerived foo = new Foo(); 
     Console.WriteLine(foo.Method()); 
     Console.WriteLine(GenericMethod<IDerived>(foo)); 
    } 

    private static string GenericMethod<T>(object foo) where T : class, IBase 
    { 
     return (foo as T).Method(); 
    } 
} 

Этот код выводит следующее:

IDerived.Method
IBase.Method

Вместо того, что один может ожидаемый:

IDerived.Method
IDerived.Method

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

EDIT: Для того, чтобы быть ясно, следующее, если проверка оценивается как истина в вызове GenericMethod выше:

, если (TypeOf (Т) == TypeOf (IDerived))

Таким образом, ответ не в том, что T всегда рассматривается как IBase из-за ограничения общего типа «где T: class, IBase».

+0

Зачем ему в первую очередь называть 'IDerived.Method'? Кроме того, одно и то же имя 'IBase.Method' и' IDerived.Method' не связаны. – PetSerAl

+0

Ваш 'GenericMethod' указывает' IBase'? – mxmissile

+0

Явная реализация интерфейса * требуется * здесь, поскольку метод() неоднозначен, их два. Если вам нравится второй результат лучше, просто улучшите свой дизайн. Либо удалить IBase из IDerived, либо удалить Method() из IDerived, поскольку IBase уже требует его реализации. И * single * Method() в классе реализует оба интерфейса без двусмысленности. –

ответ

7

Ключ здесь должен помнить, что IBase.Method и IDerived.Method - это два совершенно разных метода. Мы просто дали им похожие имена и подписи. Так как все, что реализует IDerived, также реализует IBase, что означает, что он будет иметь два метода с именем Method без параметров. Один принадлежит IDerived и один принадлежит IBase.

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

В отличие от шаблонов C++, общая подстановка не происходит всякий раз, когда метод компилируется (который с шаблонами будет выполняться один раз для каждой комбинации используемых параметров шаблона). Вместо этого метод компилируется ровно один раз таким образом, что любой тип может быть заменен во время выполнения.

В вашем случае компилятор генерирует IL для GenericMethod, который выглядит примерно так:

IL_0000: ldarg.0  
IL_0001: isinst  <T> 
IL_0006: unbox.any <T> 
IL_000B: box   <T>  
IL_0010: callvirt IBase.Method 
IL_0015: ret   

Обратите внимание, что явно вызывает IBase.Method. Нет никакого отношения виртуального/переопределения между этим методом и IDerived.Method, поэтому база - это все, что вызывается, независимо от того, какой тип заменяется на T во время выполнения.

1

Добавление к ответу Кайла, который я не могу сделать в комментарии, потому что у меня пока нет достаточной репутации ...

Я думаю, что это говорит:

private static string GenericMethod<T>(T foo) where T : class, IBase 
{ 
    return foo.Method() + " " + typeof(T) + " " + typeof(Foo); 
} 

Удаление объекта и имеющий параметр будет T, так что, как литой ненужно все еще вызывает IBase.Method.

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

C# generics не ведут себя как шаблоны C++ в этом отношении.

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

  • Нет связанных вопросов^_^