4

I've been playing around with constructors and noticed in most code overloaded constructors are:

public class ClassFirst
    {
        public string Name { get; set; }
        public int Height { get; set; }
        public int Weight { get; set; }

        public ClassFirst(string name, int height, int weight)
        {
            Name = name;
            Height = height;
            Weight = weight;

        }

        public ClassFirst(string name)
            : this(name, 0, 0)
        { }

        public ClassFirst(string name, int height)
            : this(name, height, 0)
        { }

    } 

Which I would call 'underloading' instead of overloading, because the added constructors chip away at the full constructor... and seems to be used much more than the way I intuitively want to overload, which is ->

 public class ClassSecond
    {
        public string Name { get; set; }
        public int Height { get; set; }
        public int Weight { get; set; }

        public ClassSecond(string name)
        {
            Name = name;

        }

        public ClassSecond(string name, int height): this (name)
        {

            Height = height;    
        }

        public ClassSecond(string name, int height, int weight): this (name, height)

        {
            Weight = weight;
        }
}

Why are the constructors is used like that? There must be advantages...

Answer: below is a great example of how to concisely write overloaded constructors in .net 4.0 using default parameters.

Previous Stack Overflow Answer: I found there is a previous question that addresses constructor overloads: C# constructor chaining? (How to do it?)

Stephan Luis
  • 911
  • 1
  • 9
  • 24
  • To other users considering closing this as 'primarily opinion-based': it's not. See my answer for an objective, measurable reason. – Mark Seemann Mar 03 '14 at 21:56

4 Answers4

3

Do the overloading - but supply the default parameters in the default constructor (or lowest level as the case may be):

public class ClassSecond
{
    public string Name { get; set; }
    public int Height { get; set; }
    public int Weight { get; set; }

    public ClassSecond(string name)
    {
        Name = name;
        Height = 100;
        Weight = 100;
    }

    public ClassSecond(string name, int height)
        : this(name)
    {
        Height = height;
    }

    public ClassSecond(string name, int height, int weight)
        : this(name, height)
    {
        Weight = weight;
    }
}

Due to the order of which constructors are called, you'll be setting the variables to their default values, and then later overriding them with the user-specified values. This is valid as long as your properties aren't executing some sort of logic against the setter.

That being said, as you've posted an answer I would assume you're okay with .Net 4 default parameters. In which case all of that could be replaced with:

public MyClass(string name = "Ben", int height = 100, int weight = 20)
{
    Name = name;
    Weight = weight;
    Height = height;
}

This will contain the functionality of all the overloads you've built within your question and then some.

Examples (all valid code that perform as you'd expect):

MyClass a = new MyClass();
MyClass b = new MyClass("bob");
MyClass c = new MyClass("bob", 100);
MyClass d = new MyClass("bob", 141, 300);
MyClass e = new MyClass("bob", weight: 300);
MyClass f = new MyClass(height: 50);
McAden
  • 13,714
  • 5
  • 37
  • 63
  • thanks for putting it all together, this is the most concise syntax to overload class constructors. I had a feeling that something better could be devised than what is currently documented. Have you seen this anywhere else? – Stephan Luis Mar 04 '14 at 09:48
  • Please see [this SO post](http://stackoverflow.com/questions/1814953/c-sharp-constructor-chaining-how-to-do-it?rq=1) for gotcha using default parameters "If you use this technique, you have to be aware that default arguments are set at compile time in the caller, not the callee. That means if you deploy code like this in a library and an application uses a constructor with default args; you need a re-compile of the application using the library if the default arguments change. Some people consider default arguments in public interfaces inherently dangerous because of this gotcha. – Chuu Aug " – Stephan Luis Mar 04 '14 at 09:56
  • The constructor still seems very useful to minimise code verbosity and to simplify reading... setting defaults to values not likely to change / not used in code ex. int 0 string "" will save effort. – Stephan Luis Mar 04 '14 at 10:03
1

The first one is better, because it allows you to have default values which are different then 0/false/null.

Consider following

public ClassFirst(string name, int height, int weight)
{
    Name = name;
    Height = height;
    Weight = weight;
}

public ClassFirst(string name, int height)
    : this(name, height, 150)
{ }

and now with your approach

public ClassSecond(string name, int height)
{
    Height = height;    
}

public ClassSecond(string name, int height, int weight): this (name, height)
{
    Weight = weight;
}

Where would you put 150 as default weight value? You could try

public ClassSecond(string name, int height)
{
    Height = height;
    Weight = 150;
}

but hey, we're overwriting user-specified value which was assigned in previous constructor! Conditional default value?

public ClassSecond(string name, int height)
{
    Height = height;
    Weight = Weight != 0 ? Weight : 150;
}

Looks much less readable, and yet will set weight to default value when you call ClassSecond("name", 100, 0).

Of course, you can use non-automatic properties and set default values into fields declaration directly:

private int _weight = 150;
public int Weight
{
    get { return _weight; }
    set { _weight = value; }
}

public ClassSecond(string name, int height)
{
    Height = height;    
}

public ClassSecond(string name, int height, int weight): this (name, height)
{
    Weight = weight;
}

But that's much more code, and it's not that obvious what's going on (you have to look at properties and fields declarations to check if default values are set, and what are the default values.

MarcinJuraszek
  • 124,003
  • 15
  • 196
  • 263
  • I like your answer, show the clear advantage of the first method. I would add that the object initialization syntax is often very useful as well instead of of a plethora of different constructors. – Gary Walker Mar 01 '14 at 01:09
  • @GaryWalker Sometimes (especially for immutable objects) you don't wan't the properties to be writable. – MarcinJuraszek Mar 01 '14 at 01:11
  • 1
    `but hey, we're overwriting user-specified value` - This is false. The default value is set, then the "user-specified value" overwrites it. – McAden Mar 03 '14 at 21:21
1

While it is overloading, this technique is also known as Constructor Chaining, and it has clear advantages. The most immediate advantage is that it follows the DRY (Don't Repeat Yourself) principle.

This becomes easier to see if you also start protecting the invariants of the class by adding Guard Clauses:

public string Name { get; private set; }
public int Height { get; private set; }
public int Weight { get; private set; }

public ClassFirst(string name, int height, int weight)
{
    if (name == null)
        throw new ArgumentNullException("name");
    if (height < 0)
        throw new ArgumentOutOfRangeException("height");
    if (weight < 0)
        throw new ArgumentOutOfRangeException("weight");

    Name = name;
    Height = height;
    Weight = weight;
}

public ClassFirst(string name)
    : this(name, 0, 0)
{ }

public ClassFirst(string name, int height)
    : this(name, height, 0)
{ }

The alternative is to repeat these Guard Clauses in each and every overloaded constructor. The more you add to a constructor, the greater becomes the risk that you forget to add the logic in one of the overloaded constructors.

Constructor Chaining protects you against making mistakes like that.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
0

Thanks for the constructive posts to my question. As a result I'm 'scouting' the prospects of applying the 'new' 4.0 default parameter values to constructors to solve the problem of default values that MarcinJuraszek points above. So my class looks like:

public class ClassSecond
    {
        public string Name { get; set; }
        public int Height { get; set; }
        public int Weight { get; set; }


        public ClassSecond(string name = "Ben")
        {
            Name = name;

        }

        public ClassSecond(string name = "Ben", int height = 100): this (name)
        {

            Height = height;    
        }

        public ClassSecond(string name = "Ben", int height = 100, int weight = 20): this (name, height)

        {
            Weight = weight;
        }







    }

and I get intellisense support like

Intellesenes1 enter image description here enter image description here

Are there drawbacks that I should become aware of? Does this succumb to or circumvent the versioning issues of default parameters?

Stephan Luis
  • 911
  • 1
  • 9
  • 24