2011-11-09 132 views
3

我已經閱讀了有關在C++和c#之間的構造函數或析構函數中調用的虛函數的不同行爲的一些材料。我測試下面的代碼,以確認c#可以調用虛擬衍生虛函數,因爲它的對象存在於構造函數之前。但是我發現結果與C++中的類似代碼相同。任何人都可以告訴我爲什麼c#不能顯示「22」而只顯示「12」的原因。在構造函數或析構函數中調用的虛函數的行爲

C#代碼

public class Base 
{ 
    public Base() { fun(); } 
    public virtual void fun() { Console.WriteLine(1); } 
} 
public class Derived : Base 
{ 
    public Derived() { fun(); } 
    public virtual void fun() { Console.WriteLine(2); } 
} 

C++代碼

class Base 
{ 
public: 
    Base(){fun();} 
    virtual void fun(){cout<<1;} 
}; 
class Derived : Base 
{ 
public: 
    Derived(){fun();} 
    virtual void fun(){cout<<2;} 
}; 

C++和C#的輸出結果是 「12」。

+2

您能提供一個鏈接到描述不同行爲的材料嗎? –

+0

我不是C#人員,但我相信我在實際的C#2.0標準中讀過關於這個主題的筆記。我會稍後再挖掘它。你正在使用哪種C#版本? –

+0

確實存在C#代碼錯誤,正如答案中所述。 –

回答

1

問題是,即使函數具有相同的名稱「fun()」,它們每個都是聲明它的類的成員。所以當你從基類ctor調用fun()你打電話Base.func(),當你從派生類調用fun()它就像Derived.fun();如果要輸出「22」,則需要覆蓋Derived類中的fun()

 public class Derived : Base 
    { 
     public Derived() { fun(); } 
     public override void fun() { Console.WriteLine(2); } 
    } 
+0

這隻涵蓋C# –

+0

@DaveHillier:OP已經知道不能從C++中的基類型中調用虛函數。 –

+0

@Dave Hillier問題是:「有人可以告訴我爲什麼c#不能顯示」22「而只顯示」12「的原因嗎? – mironych

0

我假設你在每種情況下都創建派生實例。

你的問題的一部分是,它不建議在構造函數期間調用虛擬方法。

  1. 調用基礎構造函數。這部分構造 做的第一件事就是初始化虛函數表funbase::fun,因此,1打印,當你調用fun()
  2. 然後 派生構造函數初始化虛函數表覆蓋fun到 點derived::fun。下一次你打電話fun()它打印2

MSDN強烈建議不要這樣:Do not call overridable methods in constructors

有效的C++製造和更強的建議:Never Call Virtual Functions during Construction or Destruction

這是一個類似question

+0

OP已經描述了他已經知道在C++基礎構造函數中不調用派生虛函數。你誤解了這個問題。他的問題是爲什麼C#也以這種方式行事,當時他被告知它與C++不同? –

2

您有一個錯誤在您的C#代碼。

要覆蓋C#函數,您必須指定override關鍵字。 如果您再次編寫virtual您正在使用基本功能,但沒有new關鍵字,您應該會收到警告。

如果兩者都聲明爲虛擬的,則有兩個具有相同名稱的函數!

讓我們做一個例子...

public class Base 
{ 
    public Base() { fun(); } 
    public virtual void fun() { Console.Write(1); } 
} 

public class Derived : Base 
{ 
    public Derived() { fun(); } 
    public override void fun() { Console.Write(2); } 
} 

public class DerivedWithError : Base 
{ 
    public DerivedWithError() { fun(); } 
    public new virtual void fun() { Console.Write(3); } 
} 

... 

// This will print "22". 
Base normal = new Derived(); 
Console.WriteLine(); 

// This will print "13" !!! 
Base withError = new DerivedWithError(); 
Console.WriteLine(); 

// Let's call the methods and see what happens! 

// This will print "2" 
normal.fun(); 
Console.WriteLine(); 

// This will print "1" !!! 
withError.fun(); 
Console.WriteLine(); 

陰影是指「具有相同的名稱添加一個新的方法,而無需使用polymorfism」。 沒有override關鍵字,您正在禁用polymorfism。

因此,一切都應該乾淨,現在很容易理解。

DerivedWithError.fun()是一種全新的虛擬方法。它在基類中具有與fun函數相同的名稱,但它們不相關!

從虛擬表(或虛擬方法表,如果您更喜歡另一個名稱)的角度來看,使用映射基函數和派生函數佔用虛表中的兩個不同條目,並且如果它們具有相同的名稱。 如果使用override來代替,則強制派生類中的func方法覆蓋由Base.func佔用的虛擬表項,這是多態性。

危險但被.NET允許,總要小心語法!

您可以在C#中的構造函數中調用虛函數,但通常在派生類中,如果在基構造函數中調用方法,則應該小心如何使用派生類中的字段。 現在,爲了保持清潔並避免混淆和風險,您應該避免在構造函數中調用虛擬方法,但實際上它幾次都非常有用。

例如所謂的「工廠方法」,它不能訪問任何變量。

public abstract class MyClass 
{ 
    public IList<string> MyList { get; private set; } 

    public MyClass() 
    { 
     this.MyList = this.CreateMyList(); 
    } 

    protected abstract IList<string> CreateMyList(); 
} 

public class MyDerived : MyClass 
{ 
    protected override IList<string> CreateMyList() 
    { 
     return new ObservableCollection<string>(); 
    } 
} 

這是完全合法的,它的工作原理!

相關問題