I have the habit of breaking the rules if it makes my code more concise or my API more convenient to use, and if I can thoughtfully get away with doing so in the specific circumstances. I'd like to know if I can get away with the following such infringement.
The following illustration includes a single Child
and single Parent
class. In fact, I am describing a common situation throughout my app. My decision regarding this problem will affect numerous classes, which is why I'm asking this question.
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...
}
Notice in the Parent
property's setter that I hand off the instance to the Parent
class. Also notice that I am invoking that setter from the public constructor.
On an earlier question I asked, it came up that handing off an instance that's not fully initialized is an anti-pattern. It may be argued that that's what's happening here. But I would say that it's not the case since basic state is initialized in the parameterless constructor which is called before the one accepting the Parent
parameter.
I thought of a problem scenario arising if a Child
subclass's constructor calls the base constructor with the Parent
parameter. If the derived constructor initializes basic state of its own then it won't be ready in time for the hand off. But I figure I can solve that problem with the sealed
keyword, if it fits the class's semantics as it does in my Child
class's cases.
So am I good to go with breaking the referenced anti-pattern in these specific circumstances? Or is there some consideration that I missed?
So am I good to go with breaking the referenced anti-pattern in these specific circumstances?
I think you're good.
I would do (have done) it myself: especially if a Child must always have a Parent, so that assigning it to the Parent is logically part of constructing the Child.
Or is there some consideration that I missed?
One difference is that if the set property throws an exception, then the object is not constructed. That makes a difference if the child is disposable.
using (Child child = new Child())
{
Child.Parent = null; // throws an exception
... etc ...
} // child.Dispose is invoked
versus
using (Child child = new Child(null)) // throws an exception
{
... etc ...
} // child.Dispose is not invoked