Microsoft Developer Network > 포럼 홈 > Visual C# Language > proper constructor chaining direction
질문하기질문하기
 

답변됨proper constructor chaining direction

  • 2007년 3월 1일 목요일 오후 4:01Flip3 사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     

    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)

     

답변

  • 2007년 3월 1일 목요일 오후 6:12Peter RitchieMVP, 중재자사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     답변됨
    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;
      }
    }

모든 응답

  • 2007년 3월 1일 목요일 오후 5:09Peter RitchieMVP, 중재자사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     
    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...
  • 2007년 3월 1일 목요일 오후 5:51Flip3 사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     

    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.

  • 2007년 3월 1일 목요일 오후 6:12Peter RitchieMVP, 중재자사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     답변됨
    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;
      }
    }
  • 2007년 3월 1일 목요일 오후 7:40Flip3 사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     

    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.

     

     

  • 2007년 3월 1일 목요일 오후 8:06Peter RitchieMVP, 중재자사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     
     Flip wrote:

    re cumulative constructors

    That's a good way to put it, is that indeed the technical term?

    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 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.

    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.

  • 2009년 7월 23일 목요일 오전 4:59quidProMS 사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     코드 있음
    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; } }
  • 2009년 7월 23일 목요일 오전 8:56Matthew Watson 사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     
    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.