12

最近我发现 C# 允许

一个接口可以从一个或多个基本接口继承

例如,IScreen在 Caliburn.Micro 中的http://caliburnmicro.codeplex.com/SourceControl/latest#src/Caliburn.Micro/IScreen.cs

namespace Caliburn.Micro
{
    public interface IScreen : IHaveDisplayName, IActivate, IDeactivate, 
        IGuardClose, INotifyPropertyChangedEx
    {
    }
}

我明白为什么这是有用的,因为它意味着实现的类IScreen也需要实现其他接口。

但我想知道 C# 如何处理该编译器和运行时。

这个问题的一点背景/背景:

我来自接口定义方法表的背景,实现接口的类既有自己的方法表,也有指向它们实现的接口的方法表的指针。

盘旋在我脑海中的子问题源于我过去与人们进行的各种多类继承讨论,我认为它们也适用于这个案例:

  • 如果一个接口能够从多个基接口继承,那么该表中方法的顺序如何?
  • 如果这些接口有共同的祖先怎么办:这些方法会在表中出现多次吗?
  • 如果这些接口有不同的祖先,但方法名称相似怎么办?

(我在这里使用了方法一词,暗示接口中定义的属性将具有 get_ 或 set_ 方法)。

非常感谢对此的任何见解,以及如何更好地表达这个问题的提示。

4

2 回答 2

9

首先,让我们明确地说“接口继承”与基于类的继承并不完全相同(并且对两者使用“继承”这个词可能会产生误导)。

这是因为接口不能自己实例化,因此编译器/运行时对不必跟踪如何对独立接口类型进行虚拟调用(例如,您不需要知道如何调用IEnumerable.GetEnumerator——您只需要知道如何在特定类型的对象上调用它)。这允许在编译时以不同的方式处理事情。

现在我实际上并不知道编译器如何实现“接口继承”,但它是如何做到的:

如果一个接口能够从多个基接口继承,那么该表中方法的顺序如何?

“派生”接口没有必要有一个方法表,其中包含来自其所有祖先接口的方法,因为它实际上并没有实现它们中的任何一个。每种接口类型只有一个它自己定义的方法表就足够了。

如果这些接口有共同的祖先怎么办:这些方法会在表中出现多次吗?

鉴于上一个问题的答案,没有。最后,一个具体类型只会实现一次,无论在实现接口的“层次结构”中出现IFoo多少次。IFoo中定义的方法IFoo只会出现在IFoo的簿记表中。

如果这些接口有不同的祖先,但方法名称相似怎么办?

再次,没问题。您需要适当的语法来告诉编译器“这里是如何实现IFoo.Frob和这里是IBar.Frob”,但是由于IFoo和的方法IBar将映射到单独的表中,因此不存在技术问题。

当然,这就留下了“如何在运行时调度方法?”的问题。没有回答。但是不难想象一个可能的解决方案:每个具体类型C都有指向它实现的每个接口的一个方法表的指针。当需要调用虚拟方法时,运行时会查看具体类型,查找要调用其方法的接口的表(接口的类型是静态已知的)并进行调用。

于 2013-11-14T10:15:47.933 回答
1

我无法谈论官方 CLR 是如何做到的。但转子分布在对象 vtable 中积极地将公共接口祖先叠加在另一个之上。它还在适当的地方将额外的 SLOT 分配到具体对象 vtable 中,从而减少从具体类型跳转到接口 vtable 再到实现的需要。方法偏移量是在 JIT 时间计算的。如果无法执行此优化,则单个方法可以多次占用 vtable。

所以答案是(无论如何关于Rotor),它确实是一个实现细节,任何覆盖/优化等都完全取决于编译器在编译类型时决定的最佳选择。

于 2013-11-14T10:36:25.570 回答