Подтвердить что ты не робот

Почему компилятор С# вставляет явную реализацию интерфейса?

Я столкнулся с странным случаем С# и искал хорошую работу.

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

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

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

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" для вывода, подразумевающего дополнительный стек стека.

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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