tags:

views:

280

answers:

2

Is it a good idea (from a design POV) to nest constructor calls for overloaded New or Factory style methods? This is mostly for simple constructors, where each overload builds on the previous one.

MyClass( arg1 ) { 
    _arg1 = arg1; 
    _otherField = true; 
    _color="Blue" 
}
MyClass( arg1, arg2) : this(arg1) { 
    _arg2 = arg2  
}
MyClass( arg1, arg2, arg3) : this(arg1, ar2) { 
    _arg3 = arg3; 
}

Or with factory methods:

static NewInstance(arg1 ) { 
   _arg1 = arg1;       
}
static NewInstance(arg1, arg2) {
   f = NewInstance(arg1);
   f._arg2 = arg2;
}
//... and so on

I can see a few drawbacks on both sides

  • Nesting hides what the constructor is doing
  • Not nesting duplicates all the functionality

So, is doing this a good idea, or does it set me up for something I'm just not seeing as a problem. For some reason I feel uneasy doing it, mostly because it divides up the responsibility for initializing.

Edit: @Jon Skeet: I see now why this was bothering me so much. I was doing it backwards! I wrote the whole thing and didn't even notice, it just smelled. Most other cases I have (that I wrote), do it the way you recommend, but this certainly isn't the only one that I have done like this. I do notice that the more complicated ones I did properly, but the simple ones I seem to have gone sloppy. I love micro edits. I also like acronymns!

+13  A: 

I think it's reasonable to chain constructors together, but I do it the other way - the version with fewer parameters calls the version with more parameters. That way it makes it very clear what's happening, and all the real "logic" (beyond the default values) is in a single place. For example:

public Foo(int x, int y)
{
    this.x = x;
    this.y = y;
    precomputedValue = x * y;
}

private static int DefaultY
{
    get { return DateTime.Now.Minute; }
}

public Foo(int x) : this(x, DefaultY)
{
}

public Foo() : this(1, DefaultY)
{
}

Note that if you have lots of constructor overloads, you may wish to move to static factory methods instead - that usually makes the code clearer, as well as allowing multiple methods to take the same set of parameters, e.g.

public static XmlDocument FromText(string xml)

public static XmlDocument FromFile(string filename)
Jon Skeet
Another set of wise words!
Gamecat
Unfortunately, this technique does not work for runtime default values. In the example, you show constants for the optional parameters. If those values were not constants, then you could not compile the code - would would have to switch to providing the values in the constructor bodies.
JeremyDWill
Nope, they can be runtime values fetched by executing static methods/properties. Edited answer to give an example.
Jon Skeet
You still moved the values to a method body, just not the constructor body. That really does not change the situation.
JeremyDWill
@Jeremy: I don't see your object, I'm afraid. The value of the "default" for y is clearly evaluated at execution time - it relies on the current date. It's not a constant. Please clarify what you believe can't be done.
Jon Skeet
@Jon: not enough space here for a full example. My basic point is that you can't inline runtime default values - you must create additional methods, which then matches the OPs example, just with a re-ordering of the call chain. The constructor calls only accept static or const value sources.
JeremyDWill
You have to be able to evaluate the value in a single expression, yes - but at that point it can be inlined. (For instance, I could have put "DateTime.Now.Minute" in the "this" call directly.) I don't really see how that relates to the OP's example though, which has the assignments scattered around.
Jon Skeet
You're scattering the evaluations instead of the assignments - trading one decentralization for another. To obtain runtime default values, you have to create separate methods, unless you're lucky enough to already have a static method created for that value. I just didn't see how that helped.
JeremyDWill
The difference being that the evaluations are named - so it's still obvious what's going on, if you name things appropriately. You don't need to look at each constructor in detail. In my experience this is rarely required anyway, to be honest - either constants or single-expressions are more common.
Jon Skeet
To each their own, I guess. In my experience, the default values come from files, databases, or the results of calculations. So creating extra static methods just to serve this construction model would be a poorer practice than performing the assignments in the constructors. Thanks for the dialog!
JeremyDWill
A: 

If you're developing in .Net 3.5 I recommend never using constructors. The only exception I leave for this is if you are using an injection constructor for dependency injection. With .Net 3.5 they created object initializers which allow you to do

var myclass = New MyClass { arg1 = "lala", arg2 ="foo" }

This will initalize the class with values assigned for arg1 and arg2 while leaving arg3 as

default(typeof(arg3)).
Chris Marisic
And bang goes any opportunity to use readonly variables...
Jon Skeet
If you're using readonly members that is a perfect target to be using Dependency Injection which falls right into my answer.
Chris Marisic
I don't regard strings and integers dependencies - but they're very often readonly members. For instance your "MyClass" example could certainly have desired arg1 and arg2 to be readonly. Fortunately with optional and named parameters, C# 4 will make this slightly easier.
Jon Skeet
So... what you are saying is to use constructors with named values, basically? Oh, and I really like exposing all my private internals just so I can get an unglier initialization syntax. So great, if I need named params I'll use 'em.
Andrew Backer