proper constructor chaining direction
I was looking at some code at my work and with several overloaded constructors having the same code in each one, I thought, beautiful, perfect place to use constructor chaining. Except I'm not sure which direction is the proper way to go?
What I mean is, in the constructor, do you go from the general to the specific, using nulls for the missing parameters, or do you go from specific to general and then in the body of the constructor set the missing properties?
Where things get interesting is when you want to call the base constructor, should it go in one place? If you're using constructor chaining, then you can do this, but where does it go, at the top or the bottom of the chain?
Here is a sample class that illustrates what I mean.
namespace ConstructorChaining
{
class Base
{
public StringBuilder orderOfExecution = new StringBuilder(); public Base(){
orderOfExecution.Append(
"Base()\n");}
public Base(String OrderOfExecution){
orderOfExecution.Append(
"Base(String " + OrderOfExecution + ")\n");}
}
class ConstructorChaining : Base{
public ConstructorChaining():
this(null) //calling the more specialized constructor passing null to the more specific constructor{
orderOfExecution.Append(
"GeneralToSpecific()\n");}
public ConstructorChaining(String NewOrderOfConstruction){
orderOfExecution.Append(
"GeneralToSpecific(String " + (NewOrderOfConstruction == null ? "NULL" : NewOrderOfConstruction) + ")\n");}
}
class SpecificToGeneral : Base{
public SpecificToGeneral(){
orderOfExecution.Append(
"SpecificToGeneral()\n");}
public SpecificToGeneral(String NewOrderOfConstruction):
this() //call the general case of the constructor{
orderOfExecution.Append(
"SpecificToGeneral(String " + (NewOrderOfConstruction == null ? "NULL" : NewOrderOfConstruction) + ")\n");}
}
}
Code to run it is here
namespace
ConstructorChaining{
public partial class Form1 : Form{
public Form1(){
InitializeComponent();
Console.WriteLine("General To Specific"); ConstructorChaining generalToSpecific1 = new ConstructorChaining(); Console.WriteLine(generalToSpecific1.orderOfExecution.ToString()); ConstructorChaining generalToSpecific2 = new ConstructorChaining("InstantiatingVariable"); Console.WriteLine(generalToSpecific2.orderOfExecution.ToString()); Console.WriteLine("Specific To General"); SpecificToGeneral specificToGeneral1 = new SpecificToGeneral(); Console.WriteLine(specificToGeneral1.orderOfExecution); SpecificToGeneral specificToGeneral2 = new SpecificToGeneral("InstantiatingVariable"); Console.WriteLine(specificToGeneral2.orderOfExecution);}
}
}
And the sample output is
General To Specific
Base()
GeneralToSpecific(String NULL)
GeneralToSpecific()
Base()
GeneralToSpecific(String InstantiatingVariable)
Specific To General
Base()
SpecificToGeneral()
Base()
SpecificToGeneral()
SpecificToGeneral(String InstantiatingVariable)
解答
- By not overloading, I meant you don't overload a base class's constructor.
For me, I go in the most specific direction.
I generally have a set of cumulative constructors, I then chain to the most specific constructor "below" the current one.
For example:
class Person
{
int age;
String name:
public Person(String name)
{
this.name = name;
}
public Person(String name, int age)
: this(name)
{
this.age = age;
}
}
所有回覆
- I'm not sure I follow your question. There's only one base so there really isn't a "chain"; and you don't overload a constructor.
If the base's default constructor (parameter-less, if it exists) isn't applicable for the constructor of a derived class you should use whatever constructor applies in those circumstances. If a derived constructor provides no details that could apply, I would just use the default constructor. In your example using base(null), you're basically wasting cycles... re not sure you follow
I'm trying to figure out which way do people use the constructor chaining.
re don't overload constructor
hhhmmmm I don't quite agree with that. The constructor is overloaded with two signatures in my example, one for empty parameters and one for a String parameter. Did you mean overriding? The constructor of the derived class does not override the base class' constructor, they are still called when/if appropriate.
re use whatever constructor applies
I guess I'm asking, when you want to have one constructor call another (code reuse, maintenance, etc), which direction do you go, towards the more specific (applying nulls for the missing parameters) or to the more general (with specific lines of code in the constructor setting the properties, variables not set in the more general cases).
As an example, in the code I have here at work, the empty constructor initializes a db proxy object. In all the other eight constructors, they each make that exact same call, so the same code is duplicated unnecessarily IMHO. What I think should be done, is have that db proxy object initialized in one of the constructor overloaded methods (either the empty one, or the most specific constructor, the one taking the most parameters). I hope that helps to clear things up a bit?
Thanks.
- By not overloading, I meant you don't overload a base class's constructor.
For me, I go in the most specific direction.
I generally have a set of cumulative constructors, I then chain to the most specific constructor "below" the current one.
For example:
class Person
{
int age;
String name:
public Person(String name)
{
this.name = name;
}
public Person(String name, int age)
: this(name)
{
this.age = age;
}
} re don't overload base constructor
Ah, ok, I see what you mean now.
re cumulative constructors
That's a good way to put it, is that indeed the technical term?
re your Person example
That's a good one, I can see now what you mean, so then you are not pushing a null parameter forward then. Instead you're going backwards to the more specific constructor/method call and then inside that constructor setting the properties that are specialized to that constructor's parameter list. Ok, I can see that logic and it makes sense.
Thank you very much for your time and explanation.
- I've never heard the term "cumulative constructors" anywhere; I just used that to describe the multiple constructor case and how I implement it. Feel free to use the term :-).
Flip wrote: re cumulative constructors
That's a good way to put it, is that indeed the technical term?
It follows the same logic as not assigning default values to instance members (the DoNotInitializeUnnecessarily rule). No logic errors will come from using "this(null)"; it's just redundant.Flip wrote: re your Person example
That's a good one, I can see now what you mean, so then you are not pushing a null parameter forward then. Instead you're going backwards to the more specific constructor/method call and then inside that constructor setting the properties that are specialized to that constructor's parameter list. Ok, I can see that logic and it makes sense.
- i disagree with the peter ritchie way. it leads to side effects. it does not localize changes. it does not extend to initializing with other than default. In this example, the default constructor initializes id to a poison pill value that is helpful during development to spot unitialized objects. All the work is always done in the most specialized (most passed parameters) version; instead of being scattered. Also my understanding is this way is very efficient.
public class MyClass { int id;
string name;
public MyClass() : this( int.MinValue, string.empty ) { } public MyClass( int Id) : this( Id, string.empty) { } public MyClass( int Id, string Name) {
id = Id;
name = Name; } } - I'm not sure why you'd need a "poison pill" default constructor - if you omit the default constructor altogether then no user code can create an object that way, so you'd never need to check that situation.

