2015-04-22 34 views
0

您無法將this作爲參數傳遞給基礎構造函數 - 請參閱(例如)C# language specification section 10.10.1 Constructor initializers(頁面上的最後一行)。C#:爲什麼我不能將'this'作爲構造函數參數傳遞給基類?

我不明白這個限制,並希望。在C#中,與C++相反,構造的實例已經處於實際類型和「一切正常」(儘管當然不是所有東西都已初始化;我的意思是在派生類的構造函數中調用的虛函數執行派生類的方法)。即使派生類的覆蓋將被執行並且派生類可能尚未準備好,基類允許調用自己的虛方法;這並不排除。那麼是什麼原因這個限制?在C++中,這可以有三個原因:首先,用戶或派生類應該知道他在做什麼;其次,C++的用戶應該知道他在做什麼;第三,甚至如果用戶不知道自己在做什麼的C++的理念就是給他,他需要上吊,然後協助他,當他綁結的繩子。我其實喜歡這一理念!)

什麼順便說一下,我試圖做的是在循環鏈表中構建正確的初始化列表成員。有一個基類Element字段Link指向下一個元素。有一類Head來自Element,它將成爲列表開始的高級標記,並且對整個列表具有特殊行爲。對於Element構造函數列表作爲參數的頭,我想寫如下初始化元素和正確的頭:

class Element { 
    protected Element link; 
    public Element(Element prior) 
    { 
     this.link = null; 
     prior.link = this; 
    } 
}; 

class Head : Element { 
    public Head() : base(this) {} 
}; 

(在抱怨,我應該這樣做例如不同我實際的代碼是有點更復雜 - 一個專門的稀疏數組 - 我有我的理由。)我將使用一個工廠的列表(和頭上的工廠方法添加元素),以避免這種情況,這可以說是更好的設計,但我不明白這個其他合理的方法是不合法的。

+3

您正在傳遞對未初始化對象的引用。可能發生的事故太多,語言禁止它。 –

+0

@Hans:它不是未初始化的:所有字段在調用實例構造函數之前都有其賦值(由於變量初始值設定項)或默認值([10.10.3節])(https://msdn.microsoft.com/ en-us/library/aa645606(v = vs.71).aspx))(這個事實使得它與C++不同)。在實例構造函數完成運行之前,實例可能不會被*語義*初始化 - 但是當基類構造函數在派生類中調用虛方法時,這是* ok。 – davidbak

+0

在這裏爭論的意義何在?與C#語言設計師交談。 –

回答

1

C#和.NET的設計旨在讓基類控制其不變量。基類構造函數完成後,允許派生類代碼(包括派生類構造函數)以基類允許的任何方式操作對象的基類部分。除了在基類構造函數調用之前將值存儲到其自己(派生類型)字段之外,派生類不能對構造中的對象執行任何操作,但基類合同實際上可以說「我要調用此在我的構造函數中使用虛擬方法,以及任何合法的派生類必須準備好處理這個問題「。

該設計確實會造成一些限制。最差的恕我直言是,在派生類構造代碼訪問其構造函數參數後,任何時候基礎類都不能建立控制構造過程的機制。

C#在鏈接到基礎構造函數之前計算字段初始化表達式,理論上這將允許派生類對象在基礎構造函數調用其虛擬方法之前設置它們;這樣做的結果是字段初始值表達式對正在構建的對象無能爲力。 VB.NET在基礎構造函數之後運行初始化程序,這允許更方便的訪問,但意味着如果基礎構造函數調用任何虛擬方法,字段將被單元化。就個人而言,我認爲C#方法「幾乎」有用,但無法處理不變量受構造函數參數影響的類嚴重限制了它的用處。

我真的很希望看到,順便說一下,Object將包括一個虛擬方法,它將在最大派生構造函數完成執行或拋出異常之間運行,並且時間控制返回到調用的代碼foo = new Bar();。在派生類構造函數運行之前,基類構造函數暴露了正在構建的對象的主要原因是它們沒有標準的方法來確保它們在此之後得到控制權。這樣的設計將大大改善構造器排序。儘管如此,我並不屏氣。

相關問題