Effective C#之14:Utilize Constructor Chaining

rel="File-List" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_filelist.xml"> rel="themeData" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_themedata.thmx"> rel="colorSchemeMapping" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_colorschememapping.xml">

Item 14: Utilize Constructor Chaining

Writing constructors is often a repetitive task. Many developers write the first constructor and then copy and paste the code into other constructors, to satisfy the multiple overrides defined in the class interface. Hopefully, you're not one of those. If you are, stop it. Veteran C++ programmers would factor the common algorithms into a private helper method. Stop that, too. When you find that multiple constructors contain the same logic, factor that logic into a common constructor instead. You'll get the benefits of avoiding code duplication, and constructor initializers generate much more efficient object code. The C# compiler recognizes the constructor initializer as special syntax and removes the duplicated variable initializers and the duplicated base class constructor calls. The result is that your final object executes the minimum amount of code to properly initialize the object. You also write the least code by delegating responsibilities to a common constructor.

编写构造函数经常是重复性的工作。很多开发者编写第一个构造函数,然后将代码复制粘贴到其它构造函数中去,以满足在类的接口上定义的多个重载。希望你不是这些人中的一个。如果你是的话,住手吧。老道的C++程序员会将通用的算法提炼到一个私有的辅助方法中,也住手吧。当你发现多个构造函数含有同样的逻辑时,将该逻辑提炼到一个共同的构造函数中去。这样做的好处有:避免代码重复、构造函数初始化器生成更高效的对象代码。C#编译器认出该构造函数初始化器是一个特殊的语法,会移除重复的变量初始化器和重复的基类构造函数调用。结果就是,最终的对象执行最少数量的代码来恰当的初始化对象。通过将职责委托给公用的构造函数,你编写的代码也最少。

Constructor initializers allow one constructor to call another constructor. This example shows a simple usage:

构造函数初始化器允许一个构造函数去调用另外一个。这个例子示范了简单的用法:

 

  1.    public class MyClass
  2.     {
  3.         // collection of data
  4.         private ArrayList coll;
  5.         // Name of the instance:
  6.         private string name;
  7.         public MyClass():this(0, "")
  8.         {
  9.         }
  10.         public MyClass(int initialCount):this(initialCount, "")
  11.         {
  12.         }
  13.         public MyClass(int initialCount, string name)
  14.         {
  15.             coll = (initialCount > 0) ? new ArrayList(initialCount) : new ArrayList();
  16.             name = name;
  17.         }
  18.  }

C# does not support default parameters, which would be the preferred C++ solution to this problem. You must write each constructor that you support as a separate function. With constructors, that can mean a lot of duplicated code. Use constructor chaining instead of creating a common utility routine. Several inefficiencies are present in this alternative method of factoring out common constructor logic:

对于这个问题,在C++里面,默认参数也许会是完美的解决方法,但是C#不支持默认参数。你必须将每个支持的构造函数写成单独的方法。对于构造函数,这意味着大量重复代码。使用构造函数链而不是创建公用的有用子程序。在这个有变化的方法中,将公用的构造函数逻辑提炼出来,暴露了低效率。

 

  1.    public class MyClass
  2.     {
  3.         // collection of data
  4.         private ArrayList coll;
  5.         // Name of the instance:
  6.         private string name;
  7.         public MyClass()
  8.         {
  9.             commonConstructor(0, "");
  10.         }
  11.         public MyClass(int initialCount)
  12.         {
  13.             commonConstructor(initialCount, "");
  14.         }
  15.         public MyClass(int initialCount, string Name)
  16.         {
  17.             commonConstructor(initialCount, Name);
  18.         }
  19.         private void commonConstructor(int count,string name)
  20.         {
  21.             coll = (count > 0) ? new ArrayList(count) : new ArrayList();
  22.             name = name;
  23.         }
  24.  }

That version looks the same, but it generates far less efficient object code. The compiler adds code to perform several functions on your behalf in constructors. It adds statements for all variable initializers (see Item 12). It calls the base class constructor. When you write your own common utility function, the compiler cannot factor out this duplicated code. The IL for the second version is the same as if you'd written this:

这个版本看起来一样,但是它生成了远远低效的对象代码。编译器为了你,在构造函数里面加入了代码来执行一些方法,为所有的变量初始化器加入语句(Item12)。它调用了基类构造函数。当你编写自己的通用有用方法时,编译器不能提炼出这个重复代码。第二个版本的IL和你这样写是一样的:

  1. // Not legal, illustrates IL generated:
  2. public MyClass()
  3. {
  4.   private ArrayList _coll;
  5.   private string  _name;
  6.   public MyClass( )
  7.   {
  8.     // Instance Initializers would go here.
  9.     object(); // Not legal, illustrative only.
  10.     commonConstructor( 0, "" );
  11.   }
  12.   public MyClass (int initialCount)
  13.   {
  14.     // Instance Initializers would go here.
  15.     object(); // Not legal, illustrative only.
  16.     commonConstructor( initialCount, "" );
  17.   }
  18.   public MyClass( int initialCount, string Name )
  19.   {
  20.     // Instance Initializers would go here.
  21.     object(); // Not legal, illustrative only.
  22.     commonConstructor( initialCount, Name );
  23.   }
  24.   private void commonConstructor( int count,string name )
  25.   {
  26.     _coll = (count > 0 ) ?
  27.       new ArrayList(count) :
  28.       new ArrayList();
  29.     _name = name;
  30.   }
  31. }

If you could write the construction code for the first version the way the compiler sees it, you'd write this:

如果你以编译器看到的方式,为第一个版本写下了构造代码,可能是这样写的:

  1. // Not legal, illustrates IL generated:
  2. public MyClass()
  3. {
  4.   private ArrayList _coll;
  5.   private string  _name;
  6.   public MyClass( )
  7.   {
  8.     // No variable initializers here.
  9.     // Call the third constructor, shown below.
  10.     this( 0, "" ); // Not legal, illustrative only.
  11.   }
  12.   public MyClass (int initialCount)
  13.   {
  14.     // No variable initializers here.
  15.     // Call the third constructor, shown below.
  16.     this( initialCount, "" );
  17.   }
  18.   public MyClass( int initialCount, string Name )
  19.   {
  20.     // Instance Initializers would go here.
  21.     object(); // Not legal, illustrative only.
  22.     _counter = initialCount;
  23.     _name = Name;
  24.   }
  25. }

The difference is that the compiler does not generate multiple calls to the base class constructor, nor does it copy the instance variable initializers into each constructor body. The fact that the base class constructor is called only from the last constructor is also significant: You cannot include more than one constructor initializer in a constructor definition. You can delegate to another constructor in this class using this(), or you can call a base class constructor using base(). You cannot do both.

不同之处在于编译器不生成对基类构造函数的多个调用,也不会将实例变量初始化器拷贝到每个构造函数体中。基类构造函数仅仅由最后那个构造函数调用,这个事实很明显:在构造函数定义里面,不能包含多于一个的构造函数初始化器。你可以通过使用this()将工作委托给类中的另外的构造函数,或者,可以通过base()调用基类的构造函数。怎么做都行。

Still don't buy the case for constructor initializers? Then think about read-only constants. In this example, the name of the object should not change during its lifetime. This means that you should make it read-only. That causes the common utility function to generate compiler errors:

仍然不买构造函数初始化器的帐?那么想想只读的常量吧。在这个例子里,对象的名字在它的生命周期内不应该被改变。这意味着,你可能让它是只读的,会引起共同的有用方法产生编译错误。

 

  1.   public class MyClass
  2.     {
  3.         // collection of data
  4.         private ArrayList coll;
  5.         // Number for this instance
  6.         private int counter;
  7.         // Name of the instance:
  8.         private readonly string name;
  9.         public MyClass()
  10.         {
  11.             commonConstructor(0, "");
  12.         }
  13.         public MyClass(int initialCount)
  14.         {
  15.             commonConstructor(initialCount, "");
  16.         }
  17.         public MyClass(int initialCount, string Name)
  18.         {
  19.             commonConstructor(initialCount, Name);
  20.         }
  21.         private void commonConstructor(int count,string name)
  22.         {
  23.             coll = (count > 0) ?
  24.               new ArrayList(count) :
  25.               new ArrayList();
  26.             // ERROR changing the name outside of a constructor.
  27.             name = name;
  28.         }
  29.   }

C++ programmers just live with this and initialize _name in all constructors, or they cast away constness in the utility routine. C#'s constructor initializers provide a better alternative. All but the most trivial classes contain more than one constructor. Their job is to initialize all the members of an object. By their very nature, these functions have similar or, ideally, shared logic. Use the C# constructor initializer to factor out those common algorithms so that you write them once and they execute once.

C++程序员靠this生存,在所有的构造函数里面初始化name,或者,它们在有用的子程序里面抛弃常量。C#的构造函数初始化器提供了更好的改变。所有的最繁琐的类包含一个或者多个构造函数。它们的工作是初始化一个对象的所有成员。由于它们的本性,这些方法有相似的,或者理想状态下有共享的逻辑。使用C#的构造函数初始化器将这些共同的算法提炼出来,那样的话,你只写一次,它们也只执行一次。

This is the last item about object initialization in C#. That makes it a good time to review the entire sequence of events for constructing an instance of a type. You should understand both the order of operations and the default initialization of an object. You should strive to initialize every member variable exactly once during construction. The best way for you to accomplish this is to initialize values as early as possible. Here is the order of operations for constructing the first instance of a type:

这是C#里面关于对象初始化的最好一条条款。要复习所有构建对象实例的事件,现在是个很好的时间。你应该同时理解操作符的顺序和对象的默认初始化行为。你应该努力在构造函数期间,将每个成员变量准确的初始化一次。完成这个的最好的方法是尽早的初始化值。下面是构建类型的第一个实例的时候,各种操作的顺序:

1.Static variable storage is set to 0.

静态变量存储区被设置成0

2.Static variable initializers execute.

静态变量初始化器被执行

3.Static constructors for the base class execute.

基类的静态构造函数被执行

4.The static constructor executes.

静态构造函数被执行

5.Instance variable storage is set to 0.

实例变量存储区被设置为0

6.Instance variable initializers execute.

实例变量初始化器被执行

7.The appropriate base class instance constructor executes.

合适的基类实例构造函数被执行

8.The instance constructor executes

实例构造函数执行

Subsequent instances of the same type start at step 5 because the class initializers execute only once. Also, steps 6 and 7 are optimized so that constructor initializers cause the compiler to remove duplicate instructions.

同一类型的后续实例从第5步开始,因为类初始化器仅仅执行一次。同时,第6步和第7步被进行了优化,那样的话,构造器初始化器会让编译器移除重复的指令。

The C# language compiler guarantees that everything gets initialized in some way when an object gets created. At a minimum, you are guaranteed that all memory your object uses has been set to 0 when an instance is created. This is true for both static members and instance members. Your goal is to make sure that you initialize all the values the way you want and execute that initialization code only once. Use initializers to initialize simple resources. Use constructors to initialize members that require more sophisticated logic. Also factor calls to other constructors, to minimize duplication.

当一个对象被创建的时候,C#语言编译器以某些方式保证一切都被初始化。最少,可以保证在一个实例被创建的时候,你的对象使用的内存都被设置为0。对于静态成员和实例成员来说,都是正确的。目标就是:以希望的方式进行初始化,并且执行仅仅一次初始化代码。使用初始化器对简单资源进行初始化。使用构造函数来初始化具有复杂逻辑的成员。同样,提炼出对基类构造函数的调用来使重复最少。

 

你可能感兴趣的:(Effective,C#,constructor,c#,compiler,编译器,initialization,object)