2014-01-06 59 views
5

如果我的代碼更簡潔,或者我的API使用起來更方便,並且在特定情況下能夠謹慎地做到這一點,我就有違反規則的習慣。我想知道我是否可以逃避以下這種侵權行爲。在構造函數返回前關閉實例

以下插圖包括一個單獨的Child和單個Parent類。事實上,我正在描述我的應用程序中的一種常見情況。我對這個問題的決定會影響很多課程,這就是爲什麼我問這個問題。在Parent屬性的setter

public sealed class Child : INotifyPropertyChanged 
{ 
    private Child() 
    { 
     //initialize basic state 
    } 

    public Child(Parent parent) 
     : this() //invoke parameterless contructor to initialize basic state 
    { 
     this.Parent = parent; //the last thing before the public ctor exits 
    } 

    public Parent Parent 
    { 
     get { return parent; } 
     set 
     { 
      if (value != parent) { 
       parent = value; 
       // initialize state that depends on the presence of a Parent instance 
       parent.Children.Add(this); //hand off to parent class 
       OnPropertyChanged("Parent"); 
      } 
     } 
    } 

    // INotifyPropertyChanged impl... 
} 

請注意,我實例移交給了Parent類。另外請注意,我正在從公共構造函數調用該setter。

earlier question I asked上,發現將未完全初始化的實例移交是一種反模式。有人可能會認爲這就是發生了什麼。但我會說,情況並非如此,因爲基本狀態是在接受參數Parent之前調用的無參數構造函數中初始化的。

我想如果Child子類的構造函數使用Parent參數調用基構造函數,則會出現問題場景。如果派生的構造函數初始化它自己的基本狀態,那麼它將不能及時準備好切換。但我想我可以用sealed關鍵字解決這個問題,如果它適合類的語義,就像我在Child類中的情況一樣。

所以,我很好去打破在這些特定情況下引用的反模式?還是有一些我錯過的考慮?

+0

爲什麼你的孩子添加到父在屬性設置父?似乎沒有相應的代碼可以將孩子從他們不再關聯的父母中移除。我可能會傾向於將其轉變爲更明確的方法,而不是屬性設置者,但除此之外,我不能看到任何遺漏的考慮因素。 –

+0

是不是可以創建一個添加方法到父類添加一個孩子到自己? –

+0

@AdamHouldsworth我實際上有[一個處理所有的API](http://www.qnomad.com/wpf/corehelpers/)(請參閱「雙向關聯」)。 – HappyNomad

回答

1

所以我很好去打破在這些特定情況下引用的反模式?

我覺得你很好。

我會自己做(已經完成):特別是如果一個孩子必須總是有一個父母,所以分配給父母在邏輯上是構建孩子的一部分。

還是有一些我錯過了的考慮?

一個區別是,如果set屬性引發異常,則該對象不會構造。如果孩子是一次性的,這是有區別的。

using (Child child = new Child()) 
{ 
    Child.Parent = null; // throws an exception 
    ... etc ... 
} // child.Dispose is invoked 

using (Child child = new Child(null)) // throws an exception 
{ 
    ... etc ... 
} // child.Dispose is not invoked 
0

這會混淆你的圖書館的消費者。

+1

如果只有一個消費者,這個類被記錄下來,並且這個方法不是從庫外部訪問的? –

+0

然後敲你自己。 –

3

你是對的,leaking this during the constructor絕對是你應該避免的,如果可能的話。

在這種情況下,它可能罰款(因爲您已標記類作爲sealed),但爲什麼做一些看起來像一個反模式時,你可以做到這一點,而不是一個更好的辦法?

我與你的代碼的另一個挑剔是,調用者可能不希望創建一個孩子的行爲已將該孩子添加到父母的孩子,所以在這種情況下,我可能會改變你的模式來電者的代碼如下所示

var child = new Child(); 
parent.Children.Add(child); 

而且具有Children屬性添加時對孩子設定的Parent使用等等。

另外,如果你真的想保持上述邏輯流程,那麼我可能會做這樣的事情,而不是。

public sealed class Child : INotifyPropertyChanged 
{ 
    private Child() { } 

    public static Child CreateAndAddChild(Parent parent) 
    { 
     var child = new Child(); 
     child.Parent = parent; 
    } 

    public Parent Parent 
    { 
     get { return parent; } 
     set 
     { 
      parent = value; 
      parent.Children.Add(this); 
      OnPropertyChanged("Parent"); 
     } 
    } 
} 

這兩種解決方案都完全避免了在構造函數中泄漏this

+1

+1將OnPropertyChanged移動到set-property實現的末尾。 – ChrisW

+0

「這真的有多大的問題?」部分http://msmvps.com/blogs/jon_skeet/archive/2010/09/02/don-t-let-this-get-away.aspx表明該模式在實踐中經常使用。 – ChrisW

+0

@Justin - 感謝您的反饋。但是,具體來說,你的第二種選擇比我的問題中的代碼更好(除了移動了「OnPropertyChanged」之外)嗎?我之前想過這樣做,但也是一種方法。只要「危險的」代碼在公共計算機中最後出現,我就不會看到「CreateAndAddChild」有多好。 – HappyNomad