2010-09-01 45 views
96

聲明接口中的C#方法時不使用關鍵字virtual,並且在派生類中重寫而不使用override關鍵字。爲什麼C#接口方法未聲明爲抽象或虛擬?

這是有原因嗎?我認爲這只是一種語言方便,顯然CLR知道如何處理這個問題(方法在默認情況下不是虛擬的),但是還有其他技術原因嗎?

這裏是IL派生類生成:

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 virtualfinal

+2

因爲它們被聲明爲接口 – 2010-09-01 19:22:21

+2

@Muad:請參閱我的編輯。 – 2010-09-01 19:24:49

回答

128

對於接口,添加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() {} 
} 

明確:這是一個類實現一個接口,但沒有提供在類中的公共接口的接口方法的一種方式。在CIL中,該方法將是private(!),但它仍然可以從類的外部從對相應接口類型的引用中調用。顯式實現也是不可覆蓋的。這是可能的,因爲有一個CIL指令(.override)將把私有方法鏈接到它正在實現的相應接口方法。

[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 
} 

現在,考慮這個奇怪的情況:

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虛擬,即使編譯器必須經過箍做到這一點。

0

他們不是虛擬的(在我們如何看待他們,如果不是在底層實現的條款術語(密封虛擬) - 好:-)

讀到這裏其他的答案,並瞭解自己的東西它們不會覆蓋任何內容 - 接口中沒有實現。

所有的接口都提供了一個類必須遵守的「契約」 - 一個模式,如果你喜歡的話,以便調用者知道如何調用這個對象,即使他們以前從未見過這個特定的類。

這是由類然後實施接口方法,因爲它會在合同範圍內 - 虛擬或「非虛擬」(密封的虛擬結果)。

+0

此線程中的每個人都知道接口的用途。這個問題非常具體 - IL生成的*對於接口方法是虛擬的,對於非接口方法來說不是虛擬的。 – 2010-09-01 19:30:43

+5

是的,在問題被編輯後批評答案真的很容易,不是嗎? – 2010-09-01 19:41:29

66

通過CSHARP第三版從CLR引用傑弗裏Ritcher這裏

的CLR要求該接口 方法被標記爲虛擬的。如果您的 沒有在您的源代碼中明確標記虛方法爲 ,那麼 編譯器將該方法標記爲虛擬 並加密;這可以防止衍生的 類重寫接口 方法。如果您明確將 方法標記爲虛擬,則編譯器將該方法標記爲 ,該方法爲虛擬方式(並且保留 解除密封);這允許派生類 覆蓋接口方法。如果密封接口方法 ,派生類 不能覆蓋方法 。但是,派生類可以重新繼承相同的接口,並且可以使用 爲接口的方法提供自己的實現。

+24

引用並沒有說_why_接口方法的實現需要標記爲虛擬。這是因爲它在接口類型方面是多態的,所以它在_virtual_表上需要一個插槽來允許虛擬方法分派。 – 2010-09-01 20:06:26

+1

我不允許將接口方法明確標記爲虛擬,並獲取錯誤「錯誤CS0106:修飾符'虛擬'對此項無效」。使用v2.0.50727進行測試(我個人電腦上的最舊版本)。 – ccppjava 2013-08-22 10:01:45

+3

@ccppjava從下面的Jorado的評論中,您標記了正在實現接口虛擬的類成員,以允許子類重寫該類。 – 2013-09-28 19:22:27

4

在大多數其他編譯的代碼環境中,接口被實現爲vtables - 指向方法體的指針列表。通常,實現多個接口的類將在其內部編譯器的某個位置生成一個接口vtables列表,每個接口一個vtable(以便保留方法順序)。這也是COM接口通常如何實現的。

但是,在.NET中,接口並沒有作爲每個類的不同vtable實現。接口方法通過全局接口方法表進行索引,所有接口都是其中的一部分。因此,爲了使該方法實現接口方法,沒有必要聲明一個虛方法 - 全局接口方法表可以直接指向類方法的代碼地址。

即使在非CLR平臺中,爲了實現接口而聲明虛方法也不需要其他語言。 Win32上的Delphi語言就是一個例子。

10

是的,就運行時而言,接口實現方法是虛擬的。它是一個實現細節,它使接口起作用。虛擬方法在類的v-表中獲得槽,每個槽具有指向其中一個虛擬方法的指針。將對象轉換爲接口類型會生成一個指向實現接口方法的表部分的指針。現在使用接口引用的客戶端代碼會看到接口指針偏移量爲0的第一個接口方法指針,等等。

我原來的回答中所欠缺的是最終屬性的意義。它可以防止派生類重寫虛方法。派生類必須重新實現接口,實現方法shadow的基類方法。這足以實現C#語言合約,它說實現方法不是虛擬的。

如果您將示例類中的Dispose()方法聲明爲虛擬,則會看到最終的屬性被刪除。現在讓派生類可以覆蓋它。

0

<笑話>這是您可能想問問Anders Hejlsberg和其他C#設計團隊的問題。 < /笑話>

接口比類更抽象的概念,當你聲明一個實現接口的類,你只是說「之類必須從接口這些具體方法,並不要緊wheter 虛擬非虛擬重寫,只要它具有相同ID和相同類型的參數」。

支持Object Pascal(「Delphi」)和Objective-C(Mac)等接口的其他語言不需要將接口方法標記爲虛擬而不是虛擬。

但是,您可能是對的,我認爲在接口中具有特定的「虛擬」/「覆蓋」屬性可能是一個好主意,如果您想要限制實現特定接口的類方法。但是,這也意味着對於兩個接口都有一個「非虛擬」,「dontcareifvirtualornot」關鍵字。

我理解你的問題,因爲我在Java中看到類似的東西,當一個類方法必須使用「@virtual」或「@override」來確保某個方法是虛擬的。

相關問題