Why are C# interface methods not declared abstract or virtual?

C# methods in interfaces are declared without using the virtual keyword, and overridden in the derived class without using the override keyword.

Is there a reason for this? I assume that it is just a language convenience, and obviously the CLR knows how to handle this under the covers (methods are not virtual by default), but are there other technical reasons?

Here is the IL that a derived class generates:

class Example : IDisposable {
public void Dispose() { }
}


.method public hidebysig newslot virtual final
instance void  Dispose() cil managed
{
// Code size       2 (0x2)
.maxstack  8
IL_0000:  nop
IL_0001:  ret
} // end of method Example::Dispose

Notice that the method is declared virtual final in the IL.

83466 次浏览

它们不是虚拟的(就我们如何看待它们而言,如果不是就底层实现而言(密封的虚拟)——在这里阅读其他答案并亲自学习一些东西很好: -)

它们不覆盖任何内容——接口中没有实现。

接口所做的就是提供一个类必须遵守的“约定”——一个模式,如果你喜欢的话,这样调用者就知道如何调用对象,即使他们以前从未见过这个特定的类。

然后由类在契约的范围内实现接口方法-虚拟或“非虚拟”(结果是密封的虚拟)。

Quoting Jeffrey Ritcher from CLR via CSharp 3rd Edition here

CLR 需要这个接口 方法被标记为虚拟。如果 不要将该方法显式标记为 在源代码中是虚拟的,则 编译器将该方法标记为虚拟 并密封; 这可以防止派生的 类重写接口 方法。如果显式标记 method as virtual, the compiler marks 方法作为虚拟的(并离开它) unsealed); this allows a derived class 重写接口方法。如果 一个接口方法是密封的,一个 派生类不能重写 但是,派生类可以 重新继承相同的接口并且可以 提供自己的实现 接口的方法。

在大多数其他编译代码环境中,接口实现为 vtables-指向方法主体的指针列表。通常,实现多个接口的类在其内部编译器中的某个地方会生成一个接口 vtables 列表,每个接口一个 vtable (以便保留方法顺序)。这也是 COM 接口的典型实现方式。

进去。NET,但是,接口不是作为每个类的不同 vtable 实现的。接口方法通过全局接口方法表进行索引,所有接口都是全局接口方法表的一部分。因此,为了让方法实现接口方法,没有必要声明一个虚方法——全局接口方法表可以直接指向类方法的代码地址。

为了实现接口而声明一个虚方法在其他语言中也是不需要的,即使在非 CLR 平台中也是如此。Win32上的 Delphi 语言就是一个例子。

对于界面,添加 abstract,甚至 public关键字都是多余的,所以你可以省略它们:

interface MyInterface {
void Method();
}

在 CIL 中,该方法被标记为 virtualabstract

(注意,Java 允许将接口成员声明为 public abstract)。

对于实现类,有一些选项:

不可重写 : 在 C # 中,该类没有将方法声明为 virtual。这意味着它不能在派生类中重写(仅隐藏)。在 CIL 中,该方法仍然是虚拟的(但是是密封的) ,因为它必须支持关于接口类型的多态性。

class MyClass : MyInterface {
public void Method() {}
}

可重写 : 在 C # 和 CIL 中,该方法都是 virtual。它参与多态分派,可以重写它。

class MyClass : MyInterface {
public virtual void Method() {}
}

Explicit: This is a way for a class to implement an interface but not provide the interface methods in the public interface of the class itself. In the CIL the method will be private (!) but it will still be callable from outside the class from a reference to the corresponding interface type. Explicit implementations are also non-overridable. This is possible because there's a CIL directive (.override) that will link the private method to the corresponding interface method that it's implementing.

[C#]

class MyClass : MyInterface {
void MyInterface.Method() {}
}

[ CIL ]

.method private hidebysig newslot virtual final instance void MyInterface.Method() cil managed
{
.override MyInterface::Method
}

在 VB.NET 中,您甚至可以在实现类中使用接口方法名称的别名。

[ VB.NET ]

Public Class MyClass
Implements MyInterface
Public Sub AliasedMethod() Implements MyInterface.Method
End Sub
End Class

[ CIL ]

.method public newslot virtual final instance void AliasedMethod() cil managed
{
.override MyInterface::Method
}

Now, consider this weird case:

interface MyInterface {
void Method();
}
class Base {
public void Method();
}
class Derived : Base, MyInterface { }

如果在同一个程序集中声明了 BaseDerived,编译器将使 Base::Method成为虚拟的和密封的(在 CIL 中) ,即使 Base没有实现接口。

如果 BaseDerived在不同的程序集中,当编译 Derived程序集时,编译器不会改变另一个程序集,所以它将在 Derived中引入一个成员,这将是 MyInterface::Method的一个显式实现,它将把调用委托给 Base::Method

因此,您可以看到,每个接口方法实现必须支持多态行为,因此必须在 CIL 上标记为虚拟,即使编译器必须经过一系列的工作才能做到这一点。

Yes, interface implementation methods are virtual as far as the runtime is concerned. It is an implementation detail, it makes interfaces work. Virtual methods get slots in the class' v-table, each slot has a pointer to one of the virtual methods. Casting an object to an interface type generates a pointer to the section of the table that implements the interface methods. The client code that uses the interface reference now sees the first interface method pointer at offset 0 from the interface pointer, etcetera.

在我最初的回答中,我低估了 final属性的重要性。它防止派生类重写虚方法。派生类必须重新实现接口,实现方法为 影子的基类方法。这就足以实现 C # 语言的约定,即实现方法不是虚拟的。

如果您将示例类中的 Dispose ()方法声明为 Virtual,您将看到 期末考试属性被删除。现在允许派生类重写它。

接口是一个比类更抽象的概念,当你声明一个实现接口的类时,你只是说“类必须从接口中有这些特定的方法,无论是 静电干扰虚拟的非虚拟的被覆盖了,只要它有相同的 ID 和相同的类型参数”。

支持对象 Pascal (“ Delphi”)和 Objective-C (Mac)等接口的其他语言也不需要将接口方法标记为虚拟或非虚拟。

但是,您可能是对的,我认为在接口中具有特定的“虚拟”/“覆盖”属性是一个好主意,以防您想要实现特定接口的类方法 限制。但是,这也意味着对于两个接口都有一个“ non-virtual”,即“ dontcareifviralornot”关键字。

我理解你的问题,因为我在 Java 中看到了类似的东西,当一个类方法必须使用“@Virtual”或“@覆盖”来确保一个方法是虚拟的时候。