2014-01-06 28 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