Why are C# interface methods not declared abstract or virtual?
接口中的C方法声明时不使用
有什么原因吗?我认为这只是一种语言便利,显然,clr知道如何在封面下处理这一点(默认情况下,方法不是虚拟的),但是还有其他技术原因吗?
下面是派生类生成的IL:
1 2 3 4 5 6 7 8 9 10 11 12 | 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 |
注意,该方法在IL中声明为
对于接口,添加
1 2 3 | interface MyInterface { void Method(); } |
在CIL中,该方法标记为
(注意Java允许接口成员被声明为
对于实现类,有一些选项:
不可重写:在C中,类不将方法声明为
1 2 3 | class MyClass : MyInterface { public void Method() {} } |
可重写:在C和CIL中,方法都是
1 2 3 | class MyClass : MyInterface { public virtual void Method() {} } |
显式:这是类实现接口但不在类本身的公共接口中提供接口方法的方法。在CIL中,方法为
〔C〕
1 2 3 | class MyClass : MyInterface { void MyInterface.Method() {} } |
[ CIL ]
1 2 3 4 | .method private hidebysig newslot virtual final instance void MyInterface.Method() cil managed { .override MyInterface::Method } |
在vb.net中,甚至可以在实现类中对接口方法名使用别名。
[VB.NET ]
1 2 3 4 5 | Public Class MyClass Implements MyInterface Public Sub AliasedMethod() Implements MyInterface.Method End Sub End Class |
[ CIL ]
1 2 3 4 | .method public newslot virtual final instance void AliasedMethod() cil managed { .override MyInterface::Method } |
现在,考虑一下这个奇怪的案例:
1 2 3 4 5 6 7 | interface MyInterface { void Method(); } class Base { public void Method(); } class Derived : Base, MyInterface { } |
如果
如果
因此,您可以看到,每个接口方法实现都必须支持多态行为,因此必须在CIL上标记为虚拟的,即使编译器必须经历循环才能做到这一点。
在这里通过CSharp引用来自clr的Jeffrey Ritcher第三版
The CLR requires that interface
methods be marked as virtual. If you
do not explicitly mark the method as
virtual in your source code, the
compiler marks the method as virtual
and sealed; this prevents a derived
class from overriding the interface
method. If you explicitly mark the
method as virtual, the compiler marks
the method as virtual (and leaves it
unsealed); this allows a derived class
to override the interface method. If
an interface method is sealed, a
derived class cannot override the
method. However, a derived class can
re-inherit the same interface and can
provide its own implementation for the
interface’s methods.
是的,就运行时而言,接口实现方法是虚拟的。它是一个实现细节,它使接口工作。虚方法在类的v-table中获取槽,每个槽都有指向其中一个虚方法的指针。将对象强制转换为接口类型将生成指向实现接口方法的表部分的指针。使用接口引用的客户机代码现在看到第一个接口方法指针位于接口指针etcetera的偏移量0处。
在我最初的回答中,我低估了最终属性的重要性。它防止派生类重写虚方法。派生类必须重新实现接口,实现方法隐藏了基类方法。这足以实现表示实现方法不是虚拟的C语言契约。
如果将示例类中的dispose()方法声明为virtual,则会看到最后一个属性被删除。现在允许派生类重写它。
在大多数其他编译的代码环境中,接口被实现为vtables——指向方法体的指针列表。通常,实现多个接口的类在其内部编译器生成的元数据中会有一个接口vtables列表,每个接口一个vtable(以便保留方法顺序)。这就是COM接口通常也是如何实现的。
然而,在.NET中,接口并不是作为每个类的不同vtables实现的。接口方法通过全局接口方法表进行索引,所有接口都是该表的一部分。因此,不必为实现接口方法而声明方法为虚方法-全局接口方法表可以直接指向类方法的代码地址。
在其他语言中,甚至在非CLR平台中,也不需要声明方法virtual来实现接口。Win32上的Delphi语言就是一个例子。
接口是一个比类更抽象的概念,当您声明一个实现接口的类时,您只需说"类必须具有来自接口的这些特定方法,并且只要它具有相同的I.D.和相同的类型参数,不管它是静态的、虚拟的、非虚拟的、重写的"。
其他支持接口的语言,如对象pascal("delphi")和objective-c(mac),也不需要将接口方法标记为虚拟的而不是虚拟的。
但是,您可能是对的,我认为最好在接口中有一个特定的"virtual"/"override"属性,以防您想要限制实现特定接口的类方法。但是,这也意味着对两个接口都有"非虚拟"、"dontcareifvirtualornot"关键字。
我理解您的问题,因为我在Java中看到了类似的东西,当类方法必须使用"@虚拟"或"@重写"来确保方法是虚拟的。
它们不是虚拟的(从我们如何看待它们的角度来看,如果不是从底层实现(密封的虚拟)的角度来看),很好地阅读这里的其他答案并自己学习一些东西:—
它们不重写任何东西——接口中没有实现。
接口所做的一切就是提供一个类必须遵循的"契约"——一个模式,如果您愿意的话,这样调用方就知道如何调用对象,即使他们以前从未见过这个特定的类。
然后由类来实现接口方法,就像它将要实现的那样,在契约的范围内——虚拟的或"非虚拟的"(实际上是密封的虚拟的)。