简体   繁体   中英

Why does the C# compiler insert an explicit interface implementation?

I ran into a strange C# edge case and am looking for a good work-around.

There is a class that I do not control that looks like this:

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

I'd like to inherit from this class in a class I do control. Additionally, I'd like to specify an interface on it:

interface IInterfaceIDoControl
{
    void SomeMethod(string argument);
}

class ClassIDoControl : OtherCompany.ClassIDoNotControl, IInterfaceIDoControl
{
}

If all of these files are in the same assembly, everything works great:

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

But, if I move "ClassIDoNotControl" into another assembly, I don't get what I expected. Instead, I see "MyCompany.IInterfaceIDoControl.SomeMethod" for the output implying an extra stack frame.

The reason is that under the covers, the C# compiler is changing "ClassIDoControl" to look like this:

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

Is there a way to avoid this compiler-generated extra layer of indirection with explicitly implemented interfaces?

Short answer: The CLR requires that all methods that implement an interface method must be virtual ( Ecma 335 Partition II Section 12.1).

Long answer:

  • If the method in the base class is already virtual, then nothing extra is needed: the interface method can be bound to it.

  • If the method in the base class is not virtual, but in the same assembly, the sneaky compiler actually makes it virtual and final . Reflector confirms this. (“final” is the CLR terminology for “sealed” in C#.)

  • If the method in the base class is not virtual and in another assembly, then obviously the compiler can't do this because it can't modify the already-compiled assembly. Therefore, the only option here is to insert a redirect method that implements the interface method. Like all methods that implement an interface method, it too is marked virtual and final .

So the answer to your last question, “Is there a way to avoid this?”, is unfortunately no.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM