locked
Constructor Chaining RRS feed

  • General discussion

  • Alright this is a generic design question and I'd like to hear opinions with explainations/justifications attacthed to them.

    My question is: Is it better (for design, maintence, whatever pros/cons you can think of) to chain constructors up the parameter chain or down?

    Here's what I mean by up/down.
    Up means having each constructor call the construct with the next largest parameter list as seen below

    //Ex. Chaining up
    class Foo
    {
    int num;
    string str;

    public Foo():this(0){}

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

    public Foo(int n, string s)
    {
        num = n;
        str = s;
    }
    }

    Down means having each constructor call the construct with the next smallest parameter list down to the default as seen below

    //Ex. Chaining up
    class Foo
    {
    int num;
    string str;

    public Foo()
    {
       num = 0;
       str = "";
    }

    public Foo(int n):this()
    {
        num = n;
    }

    public Foo(int n, string s):this(n)
    {
        str = s;
    }
    }


    What do you all think?
    Once again, this is not problem specific, it's a general concept question, and I want REASONS WHY not just "You should always chain up" etc.  If you need me to clarify anything, let me know.
    • Changed type Harry Zhu Tuesday, June 23, 2009 9:03 AM
    Thursday, June 11, 2009 7:15 PM

All replies

  • For this specific example, the former option seems better since all the assignments are in a single overload.

    But in some cases, the latter option may be the right choice, specially if there's another constructor that will not fit into the chain that easily.


    http://blog.voidnish.com
    Thursday, June 11, 2009 7:23 PM
    Moderator
  • Here's another direction this could go...
    Which is better for adding more complicated constructors at a later date (which makes it easier that is).

    Thursday, June 11, 2009 7:27 PM
  • The latter is more adaptable when adding more constructor overloads in future.

    My previous post, I mixed them up - I have corrected that.


    http://blog.voidnish.com
    Thursday, June 11, 2009 7:29 PM
    Moderator
  • I prefer the first option. I consider it more "flexible".

    The following example only works chaining up:

    class Bar
    {
        private readonly int a;
        protected int A
        {
            get { return a; }
        }
    
        protected Bar (int a)
        {
            this.a = a;
        }
    }
    
    class Foo: Bar
    {
        private string s;
    
        public Foo() : this ("") {}
        public Foo(string s): this (s, 0) {}
        public Foo(string s, int a): base (a)
        {
            this.s = s;
        }
    }

    Regards,
    Fernando.

    I always try to Keep it Sharp & simple.
    Thursday, June 11, 2009 7:30 PM
  • I assume you intended Foo to Inherit Bar.
    Thursday, June 11, 2009 7:38 PM
  • Yes... I'll edit, yet again. :(
    I always try to Keep it Sharp & simple.
    Thursday, June 11, 2009 7:42 PM
  • In Fernando's approach, if you want to add a new ctor in Foo, Foo(int x, int a) then you'd have to add a new catch-all ctor -> Foo(string s, int x, int a) and then delegate everything else to this one.

    So neither approach is a perfect fit, and horses for courses is where this is at :-)
    http://blog.voidnish.com
    Thursday, June 11, 2009 7:42 PM
    Moderator
  • Use C# 4.0 optional parameters.
    Thursday, June 11, 2009 8:31 PM
  • Here's another consideration I just noticed.
    Each apprroach calls the constructors in a different order.

    Calling a Foo cTor will execute every ctor from the largest param list DOWN TO the one that was initially called.
    Calling a Bar cTor will execut every ctor from the defualt ctor UP TO the one that was initially called.

    Just another interesting difference I noticed.
    Thursday, June 11, 2009 8:34 PM
  • That works if you don't have any code in the constructor to execute besides the initalizations, but if you want to execute some code in the ctor like data validation for each parameter, wouldn't it make sense to compartmentalize those validations.

    BTW I asked for explainations rather than declarations like "Use C# 4.0 optional parameters."  I'm interested in the theory/reasoning behind your idea.
    Thursday, June 11, 2009 8:39 PM
  • Hi,

    As this is not problem specific question , I'm going to change the thread type to "general discussion".

    Harry
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Thursday, June 18, 2009 9:53 AM
  • Hi,

    If Foo(int n) is changed someday , we may have to change Foo() as well .
    Instead call public Foo():this(0){}, I think calling  public Foo():this(0,""){} is better. We do not need to call this(0) which will call this(n, "").

    Harry


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Thursday, June 18, 2009 10:05 AM
  • So we have "chaining up", "chaining down", and Harry suggests another one -- don't chain, instead have a master ctor and other ctors call that.

    Between chaining up and chaining down, chaining-up should be marginally faster since the same variable is not set multiple times.  Using a master cto might be faster than both the others by avoiding extra calls.  This all assumes that you are dealing with fairly simple construction.  By the way, when construction starts to become complex, this should signal the need to rethink the approach and possibly refactor.  And, when chaining starts getting crazy, that's a sign of complex construction. 

    The main purposes of using chained or mastor ctor approaches is to provide multiple interfaces for constructing an object, to compartmentalize the initialization code (because multiple interfaces are used), and to provide ease of maintenance or modification (by keeping the code logically organized and together).  A master ctor or a chaining-up approach seems to accomplish these 3 purposes.  The chaining-down seems to spread out the code more.

    But the final call is what does the class need?  You could have all kinds of ctor calling signatures defined and have to implement chaining up and down and master ctors.  Ask your self what kind of construction does the class need? or does the class need refactoring? 


    Les Potter, Xalnix Corporation, Yet Another C# Blog
    Thursday, June 18, 2009 12:02 PM