Почему компилятор C # вставляет явную реализацию интерфейса? - PullRequest
31 голосов
/ 08 сентября 2010

Я столкнулся со странным крайним случаем C # и ищу хороший обходной путь.

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

namespace OtherCompany
{
    public class ClassIDoNotControl
    {
        public void SomeMethod(string argument)
        {
            Console.WriteLine((new StackFrame(1).GetMethod().Name));
        }
    }
}

Я бы хотел наследовать от этого класса в классе I do control. Кроме того, я хотел бы указать интерфейс на нем:

interface IInterfaceIDoControl
{
    void SomeMethod(string argument);
}

class ClassIDoControl : OtherCompany.ClassIDoNotControl, IInterfaceIDoControl
{
}

Если все эти файлы находятся в одной сборке, все отлично работает:

namespace MyCompany
{
    class Program
    {
        static void Main(string[] args)
        {
            IInterfaceIDoControl i = new ClassIDoControl();
            i.SomeMethod("Hello World!"); // Prints "Main"
        }
    }
 }

Но, если я перенесу "ClassIDoNotControl" в другую сборку, я не получу то, что ожидал. Вместо этого я вижу «MyCompany.IInterfaceIDoControl.SomeMethod» для вывода, подразумевающего дополнительный кадр стека.

Причина в том, что под прикрытием компилятор C # меняет "ClassIDoControl", чтобы он выглядел так:

class ClassIDoControl : OtherCompany.ClassIDoNotControl, IInterfaceIDoControl
{
    void IInterfaceIDoControl.SomeMethod(string argument)
    {
        base.SomeMethod(argument);
    }
}

Есть ли способ избежать этого сгенерированного компилятором дополнительного уровня косвенности с явно реализованными интерфейсами?

1 Ответ

43 голосов
/ 08 сентября 2010

Краткий ответ: CLR требует, чтобы все методы, реализующие метод интерфейса, были виртуальными ( Ecma 335 Partition II Раздел 12.1).

Длинный ответ:

  • Если метод в базовом классе уже является виртуальным, то ничего лишнего не требуется: к нему может быть привязан интерфейсный метод.

  • Если метод в базовом классе не виртуальный, но в той же сборке, то хитрый компилятор фактически делает его виртуальным и окончательным . Отражатель подтверждает это. («окончательный» - это терминология CLR для «запечатанный» в C #.)

  • Если метод в базовом классе не является виртуальным и находится в другой сборке, то, очевидно, компилятор не может этого сделать, поскольку он не может изменять уже скомпилированную сборку. Поэтому единственный вариант здесь - вставить метод перенаправления, который реализует метод интерфейса. Как и все методы, реализующие интерфейсный метод, он также помечается как virtual и final .

Итак, ответ на ваш последний вопрос: «Есть ли способ избежать этого?», К сожалению, нет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...