How to do constructor chaining in C#
Asked Answered
O

9

271

I know that this is supposedly a super simple question, but I've been struggling with the concept for some time now.

My question is, how do you chain constructors in C#?

I'm in my first OOP class, so I'm just learning. I don't understand how constructor chaining works or how to implement it or even why it's better than just doing constructors without chaining.

I would appreciate some examples with an explanation.

So how do how chain them?
I know with two it goes:

public SomeClass this: {0}

public SomeClass
{
    someVariable = 0
} 

But how do you do it with three, four and so on?

Again, I know this is a beginner question, but I'm struggling to understand this and I don't know why.

Ox answered 29/11, 2009 at 7:56 Comment(0)
R
421

You use standard syntax (using this like a method) to pick the overload, inside the class:

class Foo 
{
    private int id;
    private string name;

    public Foo() : this(0, "") 
    {
    }

    public Foo(int id, string name) 
    {
        this.id = id;
        this.name = name;
    }

    public Foo(int id) : this(id, "") 
    {
    }

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

then:

Foo a = new Foo(), b = new Foo(456,"def"), c = new Foo(123), d = new Foo("abc");

Note also:

  • you can chain to constructors on the base-type using base(...)
  • you can put extra code into each constructor
  • the default (if you don't specify anything) is base()

For "why?":

  • code reduction (always a good thing)
  • necessary to call a non-default base-constructor, for example:

    SomeBaseType(int id) : base(id) {...}
    

Note that you can also use object initializers in a similar way, though (without needing to write anything):

SomeType x = new SomeType(), y = new SomeType { Key = "abc" },
         z = new SomeType { DoB = DateTime.Today };
Rabbitfish answered 29/11, 2009 at 8:6 Comment(6)
I'm doing some chaining and have to ask a question since this answer was voted so highly. Is there any disadvantage to having each constructor set the properties that were passed to it, and then call the default constructor to set the others? This way you aren't coding default values (0 and "") in more than one place (less chance for an error). For example: public Foo(int id) : this () { this.id = id; }? Alternatively, I was also considering: public Foo(int id) : this ("") { this.id = id; }. Just looking for the best logical way to chain them, appreciate any thoughts.Hasa
Is there a way to manipulate the argument values of the to be called constructor in the first constructor before the other chained constructor is called?Sanious
In which order are the constructors executed?Dextro
@Nanoserver base firstRabbitfish
@MarcGravell Thank you very much. I should have been more clear. I meant with this.Dextro
callee, then caller - i.e. the chained constructor firstRabbitfish
A
62

I just want to bring up a valid point to anyone searching for this. If you are going to work with .NET versions before 4.0 (VS2010), please be advised that you have to create constructor chains as shown above.

However, if you're staying in 4.0, I have good news. You can now have a single constructor with optional arguments! I'll simplify the Foo class example:

class Foo {
  private int id;
  private string name;

  public Foo(int id = 0, string name = "") {
    this.id = id;
    this.name = name;
  }
}

class Main() {
  // Foo Int:
  Foo myFooOne = new Foo(12);
  // Foo String:
  Foo myFooTwo = new Foo(name:"Timothy");
  // Foo Both:
  Foo myFooThree = new Foo(13, name:"Monkey");
}

When you implement the constructor, you can use the optional arguments since defaults have been set.

I hope you enjoyed this lesson! I just can't believe that developers have been complaining about construct chaining and not being able to use default optional arguments since 2004/2005! Now it has taken SO long in the development world, that developers are afraid of using it because it won't be backwards compatible.

Amary answered 13/1, 2011 at 14:36 Comment(4)
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.Katlin
Another drawback with default arguments approach is that if you have for example two default args in constructor, you cannot call it only with the second one. In the example here, you would have compile error for: Foo myFooOne = new Foo("");Sobriquet
Also a problem if one of your constructors is relying on dependency injectionGq
If you need dependency injection, as John Arundell pointed, the error msg will be "parameter must be a compiler time constant"; for circumvent that, do the old chaining way, or pass an Options object, as Jon Skeet's answer.Countrified
P
34

This is best illustrated with an example. Imaging we have a class Person

public Person(string name) : this(name, string.Empty)
{
}

public Person(string name, string address) : this(name, address, string.Empty)
{
}

public Person(string name, string address, string postcode)
{
    this.Name = name;
    this.Address = address;
    this.Postcode = postcode;
}

So here we have a constructor which sets some properties, and uses constructor chaining to allow you to create the object with just a name, or just a name and address. If you create an instance with just a name this will send a default value, string.Empty through to the name and address, which then sends a default value for Postcode through to the final constructor.

In doing so you're reducing the amount of code you've written. Only one constructor actually has code in it, you're not repeating yourself, so, for example, if you change Name from a property to an internal field you need only change one constructor - if you'd set that property in all three constructors that would be three places to change it.

Polychaete answered 29/11, 2009 at 8:9 Comment(0)
C
16

What is usage of "Constructor Chain"?
You use it for calling one constructor from another constructor.

How can implement "Constructor Chain"?
Use ": this (yourProperties)" keyword after definition of constructor. for example:

Class MyBillClass
{
    private DateTime requestDate;
    private int requestCount;

    public MyBillClass()
    {
        /// ===== we naming "a" constructor ===== ///
        requestDate = DateTime.Now;
    }
    public MyBillClass(int inputCount) : this()
    {
        /// ===== we naming "b" constructor ===== ///
        /// ===== This method is "Chained Method" ===== ///
        this.requestCount= inputCount;
    }
}

Why is it useful?
Important reason is reduce coding, and prevention of duplicate code. such as repeated code for initializing property Suppose some property in class must be initialized with specific value (In our sample, requestDate). And class have 2 or more constructor. Without "Constructor Chain", you must repeat initializaion code in all constractors of class.

How it work? (Or, What is execution sequence in "Constructor Chain")?
in above example, method "a" will be executed first, and then instruction sequence will return to method "b". In other word, above code is equal with below:

Class MyBillClass
{
    private DateTime requestDate;
    private int requestCount;

    public MyBillClass()
    {
        /// ===== we naming "a" constructor ===== ///
        requestDate = DateTime.Now;
    }
    public MyBillClass(int inputCount) : this()
    {
        /// ===== we naming "b" constructor ===== ///
        // ===== This method is "Chained Method" ===== ///

        /// *** --- > Compiler execute "MyBillClass()" first, And then continue instruction sequence from here
        this.requestCount= inputCount;
    }
}
Commemoration answered 13/12, 2016 at 6:34 Comment(0)
H
12

I have a diary class and so i am not writing setting the values again and again

public Diary() {
    this.Like = defaultLike;
    this.Dislike = defaultDislike;
}

public Diary(string title, string diary): this()
{
    this.Title = title;
    this.DiaryText = diary;
}

public Diary(string title, string diary, string category): this(title, diary) {
    this.Category = category;
}

public Diary(int id, string title, string diary, string category)
    : this(title, diary, category)
{
    this.DiaryID = id;
}
Haugh answered 18/7, 2012 at 16:13 Comment(0)
G
9

All those answers are good, but I'd like to add a note on constructors with a little more complex initializations.

class SomeClass {
    private int StringLength;

    SomeClass(string x) {
         // this is the logic that shall be executed for all constructors.
         // you dont want to duplicate it.
         StringLength = x.Length;
    }

    SomeClass(int a, int b): this(TransformToString(a, b)) {
    }

    private static string TransformToString(int a, int b) {
         var c = a + b;
         return $"{a} + {b} = {c}";
    }
}

Allthogh this example might as well be solved without this static function, the static function allows for more complex logic, or even calling methods from somewhere else.

Galosh answered 24/11, 2020 at 14:22 Comment(1)
Thank you! Exactly the example that was missing from all the other answers.Sycosis
K
6

Are you asking about this?

  public class VariantDate {
    public int day;
    public int month;
    public int year;

    public VariantDate(int day) : this(day, 1) {}

    public VariantDate(int day, int month) : this(day, month,1900){}

    public VariantDate(int day, int month, int year){
    this.day=day;
    this.month=month;
    this.year=year;
    }

}
Kristynkrock answered 29/11, 2009 at 8:7 Comment(0)
N
2

I hope following example shed some light on constructor chaining.
my use case here for example, you are expecting user to pass a directory to your constructor, user doesn't know what directory to pass and decides to let you assign default directory. you step up and assign a default directory that you think will work.

BTW, I used LINQPad for this example in case you are wondering what *.Dump() is.
cheers

void Main()
{

    CtorChaining ctorNoparam = new CtorChaining();
    ctorNoparam.Dump();
    //Result --> BaseDir C:\Program Files (x86)\Default\ 

    CtorChaining ctorOneparam = new CtorChaining("c:\\customDir");
    ctorOneparam.Dump();    
    //Result --> BaseDir c:\customDir 
}

public class CtorChaining
{
    public string BaseDir;
    public static string DefaultDir = @"C:\Program Files (x86)\Default\";


    public CtorChaining(): this(null) {}

    public CtorChaining(string baseDir): this(baseDir, DefaultDir){}

    public CtorChaining(string baseDir, string defaultDir)
    {
        //if baseDir == null, this.BaseDir = @"C:\Program Files (x86)\Default\"
        this.BaseDir = baseDir ?? defaultDir;
    }
}
Nympholepsy answered 11/4, 2013 at 0:8 Comment(0)
L
2

There's another important point in constructor chaining: order. Why? Let's say that you have an object being constructed at runtime by a framework that expects it's default constructor. If you want to be able to pass in values while still having the ability to pass in constructor argments when you want, this is extremely useful.

I could for instance have a backing variable that gets set to a default value by my default constructor but has the ability to be overwritten.

public class MyClass
{
  private IDependency _myDependency;
  MyClass(){ _myDependency = new DefaultDependency(); }
  MYClass(IMyDependency dependency) : this() {
    _myDependency = dependency; //now our dependency object replaces the defaultDependency
  }
}
Layla answered 18/12, 2015 at 12:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.