2013-01-24 46 views
1
class Program 
{ 
    static void Main(string[] args) 
    { 
     B foo = new B(); 
     foo.DoWork(); 
     Console.ReadLine(); 
    } 
} 

public class A 
{ 
    public virtual void DoWork() { Console.WriteLine("A"); } 
} 
public class B : A 
{ 
    public override void DoWork() { base.DoWork(); Console.WriteLine("B"); } 
} 

爲什麼我不能得到StackOverflow異常?據我瞭解,foo.DoWork()被調用,然後它調用base.DoWork(),它是虛擬的,並在類B.DoWork()方法中重寫,它會重複調用base.DoWork(),直到堆棧溢出。當使用這個而不是base(循環調用self)時,這個溢出很容易實現。在這種情況下,防止虛函數覆蓋的是什麼?爲什麼在覆蓋和調用虛擬函數時不能獲得stackoverflow異常?

+0

如果這就是'base'的工作方式,那麼任何時候都會使用它,這將是一個計算器,並且根本沒有理由在語言中使用它。 – Servy

+0

@Servy不,你可以用沒有任何問題的基礎調用非虛擬方法。這只是使用虛擬方法的基礎的特殊情況。 – user206334

+0

如果您想調用基類的非虛方法,只需將'this'強制轉換爲基類並調用該方法即可。 'base'的設計專門用於調用基類的虛擬方法版本,因爲它不能在沒有'base'的情況下完成,其他任何'base'的使用都可以在沒有該關鍵字的情況下完成。 – Servy

回答

11

不,當您使用base不會發出虛擬呼叫。即使您重寫了,整個要點也可以調用base實現

如果在產生IL一看,你會看到它不使用callvirt

IL_0002: call  instance void A::DoWork() 

從C#5規範(重點煤礦)第7.6.8:

當基礎訪問引用虛擬功能成員(方法,屬性或索引器)時,決定在運行時調用哪個功能成員(第7.5.4節)會發生變化。 被調用的函數成員是通過查找函數成員關於B的最派生的實現(第10.6.3節)來確定的(而不是相對於此的運行時類型,如通常在非基礎訪問)。因此,在對虛擬函數成員的重寫中,可以使用基訪問來調用函數成員的繼承實現。如果基本訪問所引用的函數成員是抽象的,則會發生綁定時錯誤。

+0

[Eric Lippert寫道](http://blogs.msdn.com/b/ericlippert/archive/2010/03/29/putting-a-base-in-the-middle.aspx)關於事實的後果C#去找到實際上具有實現的派生基類,而不是直接基類。 –

+0

@BenVoigt :(我發現我們幾乎在同一時間發現:) –

+0

是的。我只是認爲討論似乎與這個答案有關。 –

1

虛擬方法只是一種可以被重寫幷包含代碼的方法。當您致電base.DoWork()時,您明確指出您想致電A.DoWork()。然後,調用A.DoWork()

試着製作A.DoWork()抽象然後它不能包含代碼。然後,您將在base.DoWork()上發生編譯錯誤,因爲在base.DoWork()中沒有任何內容可以執行。

+1

重載!=覆蓋。你的意思是在這裏重寫。 –

2

A.DoWork是虛擬的。但是一個名爲base.的方法是從來沒有虛擬。該語法生成​​非虛擬調用,因此調用了確切的方法,而不是最派生的版本。

相關問題