我遇到了一个奇怪的C#边缘案例,我正在寻找一个好的解决方案。
有一个我无法控制的课程,如下所示:
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”。
原因在于,C#编译器正在将“ClassIDoControl”更改为如下所示:
class ClassIDoControl : OtherCompany.ClassIDoNotControl, IInterfaceIDoControl
{
void IInterfaceIDoControl.SomeMethod(string argument)
{
base.SomeMethod(argument);
}
}
有没有办法通过显式实现的接口来避免这个编译器生成的额外的间接层?
答案 0 :(得分:43)
简答: CLR要求实现接口方法的所有方法都必须是虚拟的(Ecma 335 Partition II第12.1节)。
答案很长:
如果基类中的方法已经是虚拟的,那么就不需要额外的东西了:接口方法可以绑定到它。
如果基类中的方法不是虚拟,但是在同一个程序集中,那么偷偷摸摸的编译器实际上使其成为虚拟和最终。 Reflector证实了这一点。 (“final”是C#中“密封”的CLR术语。)
如果基类中的方法不是虚拟的并且在另一个程序集中,那么很明显编译器无法执行此操作,因为它无法修改已编译的程序集。因此,此处唯一的选择是插入实现接口方法的重定向方法。与实现接口方法的所有方法一样,它也标记为虚拟和最终。
所以回答你的上一个问题,“有没有办法避免这种情况?”,遗憾的是没有。