我遇到了一个奇怪的 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);
}
}
有没有办法通过显式实现的接口来避免编译器生成的额外间接层?
简短回答: CLR 要求所有实现接口方法的方法都必须是虚拟的(Ecma 335 Partition II 第 12.1 节)。
长答案:
如果基类中的方法已经是虚拟的,那么不需要任何额外的操作:接口方法可以绑定到它。
如果基类中的方法是不是虚拟的,但在同一个程序集中,狡猾的编译器实际上使其成为虚拟的和最终的。反射器证实了这一点。 (“final”是 C# 中“sealed”的 CLR 术语。)
如果基类中的方法不是虚拟的并且在另一个程序集中,那么显然编译器无法执行此操作,因为它无法修改已编译的程序集。因此,这里唯一的选择是插入一个实现接口方法的重定向方法。与实现接口方法的所有方法一样,它也被标记为 virtual 和 Final。
因此,不幸的是,你最后一个问题“有没有办法避免这种情况?”的答案是否定的。