为什么 C# 编译器要插入显式接口实现?

问题描述 投票:0回答:1

我遇到了一个奇怪的 C# 边缘情况,正在寻找一个好的解决方法。

有一个我无法控制的类,如下所示:

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

我想在我do控制的类中继承该类。另外,我想在上面指定一个接口:

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

有没有办法通过显式实现的接口来避免编译器生成的额外间接层?

c# interface
1个回答
43
投票

简短回答: CLR 要求所有实现接口方法的方法都必须是虚拟的(Ecma 335 Partition II 第 12.1 节)。

长答案:

  • 如果基类中的方法已经是虚拟的,那么不需要任何额外的操作:接口方法可以绑定到它。

  • 如果基类中的方法是不是虚拟的,但在同一个程序集中,狡猾的编译器实际上使其成为虚拟的和最终的。反射器证实了这一点。 (“final”是 C# 中“sealed”的 CLR 术语。)

  • 如果基类中的方法不是虚拟的并且在另一个程序集中,那么显然编译器无法执行此操作,因为它无法修改已编译的程序集。因此,这里唯一的选择是插入一个实现接口方法的重定向方法。与实现接口方法的所有方法一样,它也被标记为 virtual 和 Final

因此,不幸的是,你最后一个问题“有没有办法避免这种情况?”的答案是否定的。

© www.soinside.com 2019 - 2024. All rights reserved.