Objective C # principle 14: Using constructor chains
Item 14: Utilize constructor chaining
Writing constructor is a repetitive task. Many developers write a constructor first, and then copy and paste it to other constructor to satisfy some of the class's heavy-load interfaces. I hope you didn't do this. If so, stop it. Experienced C ++ProgramA secondary private method may be usedAlgorithmPut it inside to construct the object. Please stop. When you find that multiple constructors contain the same logic, instead, they are put in a common constructor. You can avoidCodeAnd constructor Initialization is more efficient than other code of the object. C # The Compiler recognizes constructor initialization as a special syntax and removes repeated variables and repeated base class constructor from the preset methods. The result is that your object will eventually execute the least code to initialize the object reasonably. You can also write the least code to delegate the responsibility to a common constructor. The preset method of the constructor allows one constructor to call another constructor. This is a simple example:
Public class myclass
{
// Collection of data
Private arraylist _ Coll;
// Name of the instance:
Private string _ name;
Public myclass ():
This (0 ,"")
{
}
Public myclass (INT initialcount ):
This (initialcount ,"")
{
}
Public myclass (INT initialcount, string name)
{
_ Coll = (initialcount> 0 )?
New arraylist (initialcount ):
New arraylist ();
_ Name = Name;
}
}
C # does not support parameters with default values. c ++ is a good solution to this problem, so as to effectively reduce the function overload ). You must override every special constructor. Such constructor means that a large amount of code is repetitive. You can use the constructor chain to replace the conventional method. The following are some conventional inefficient constructor logic:
Public class myclass
{
// Collection of data
Private arraylist _ Coll;
// Name of the instance:
Private string _ name;
Public myclass ()
{
Commonconstructor (0 ,"");
}
Public myclass (INT initialcount)
{
Commonconstructor (initialcount ,"");
}
Public myclass (INT initialcount, string name)
{
Commonconstructor (initialcount, name );
}
Private void commonconstructor (INT count,
String name)
{
_ Coll = (count> 0 )?
New arraylist (count ):
New arraylist ();
_ Name = Name;
}
}
This version looks the same, but the generation efficiency is far less than other code of the object. For your benefit, the compiler adds some code to the constructor. Added some code to initialize all the variables (see Principle 12 ). It also calls the constructors of the base class. When you write some valid functions by yourself, the compiler will not add the repeated code. The IL code of the second version is the same as that written below:
// Not legal, using strates il generated:
Public myclass ()
{
Private arraylist _ Coll;
Private string _ name;
Public myclass ()
{
// Instance initializers wocould go here.
Object (); // not legal, legal strative only.
Commonconstructor (0 ,"");
}
Public myclass (INT initialcount)
{
// Instance initializers wocould go here.
Object (); // not legal, legal strative only.
Commonconstructor (initialcount ,"");
}
Public myclass (INT initialcount, string name)
{
// Instance initializers wocould go here.
Object (); // not legal, legal strative only.
Commonconstructor (initialcount, name );
}
Private void commonconstructor (INT count,
String name)
{
_ Coll = (count> 0 )?
New arraylist (count ):
New arraylist ();
_ Name = Name;
}
}
If you use the first version to write the constructor, you can write the constructor as follows:
// Not legal, using strates il generated:
Public myclass ()
{
Private arraylist _ Coll;
Private string _ name;
Public myclass ()
{
// No variable initializers here.
// Call the third constructor, shown below.
This (0, ""); // not legal, legal strative only.
}
Public myclass (INT initialcount)
{
// No variable initializers here.
// Call the third constructor, shown below.
This (initialcount ,"");
}
Public myclass (INT initialcount, string name)
{
// Instance initializers wocould go here.
Object (); // not legal, legal strative only.
_ Counter = initialcount;
_ Name = Name;
}
}
The difference is that the compiler does not generate multiple calls to the base class and does not copy instance variables to every constructor. In fact, the base class constructor is only called in the last constructor. This is equally important: you cannot include more constructor preset methods. In this class, you can use this () to delegate it to another method, or you can use base () to call the construction of the base class. But you cannot call two at the same time.
Isn't the pre-configured method of the constructor clear? Consider the read-only constants. In this example, the object name should not be changed throughout the life cycle. That is to say, you should set it to read-only. If you use a helper function to construct an object, a compilation error is returned:
Public class myclass
{
// Collection of data
Private arraylist _ Coll;
// Number for this instance
Private int _ counter;
// Name of the instance:
Private readonly string _ name;
Public myclass ()
{
Commonconstructor (0 ,"");
}
Public myclass (INT initialcount)
{
Commonconstructor (initialcount ,"");
}
Public myclass (INT initialcount, string name)
{
Commonconstructor (initialcount, name );
}
Private void commonconstructor (INT count,
String name)
{
_ Coll = (count> 0 )?
New arraylist (count ):
New arraylist ();
// Error changing the name outside of a constructor.
_ Name = Name;
}
}
The C ++ program will leave the _ name in every constructor, or usually discard it in the Helper function. The C # constructor provides a good choice. Almost all trivial classes contain more than one constructor. Their job is to initialize all the member variables of the object. This is common, and these functions have similar shared logical structures ideally. Use C # To construct the preset method to generate these general algorithms, so that only one write and only one execution will be performed.
This is the last principle about Object Construction in C #. It is time to review the entire event sequence of a type during construction. You need to understand the operation sequence of an object and the default preset method sequence at the same time. During your construction, you should try to make all the member variables initialize exactly once. The best way to accomplish this goal is to complete variable initialization as soon as possible. This is the sequence in which an instance is constructed for a type for the first time:
1. The storage location of static variables is 0.
2. Execute the static variable preset method.
3. base class static constructor execution.
4. Static constructor execution.
5. The storage location of instance variables is 0.
6. Run the instance variable preset method.
7. Proper base class instance constructor execution.
8. Execute the instance constructor.
Subsequent instances of the same type start from step 1, because the class preset method is only executed once. Similarly, steps 6th and 7th are optimized, allowing the compiler to remove duplicate commands from the constructor's preset methods.
The C # compiler ensures that all things are generated in the same way during initialization. At least, you should ensure that all the memory occupied by the object is set to 0 when you create your type. Static and instance members are the same. Your goal is to ensure that the initialization code you want to execute is executed only once. Use the preset method to initialize simple resources, and use constructors to initialize some members with complex logical structures. Similarly, to reduce the number of duplicates, call other constructors as much as possible.
==================================
Item 14: Utilize constructor chaining
Writing constructors is often a repetitive task. developers 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 wocould 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 cballs. 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.
Constructor initializers allow one constructor to call another constructor. This example shows a simple usage:
Public class myclass
{
// Collection of data
Private arraylist _ Coll;
// Name of the instance:
Private string _ name;
Public myclass ():
This (0 ,"")
{
}
Public myclass (INT initialcount ):
This (initialcount ,"")
{
}
Public myclass (INT initialcount, string name)
{
_ Coll = (initialcount> 0 )?
New arraylist (initialcount ):
New arraylist ();
_ Name = Name;
}
}
C # does not support default parameters, which wocould 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:
Public class myclass
{
// Collection of data
Private arraylist _ Coll;
// Name of the instance:
Private string _ name;
Public myclass ()
{
Commonconstructor (0 ,"");
}
Public myclass (INT initialcount)
{
Commonconstructor (initialcount ,"");
}
Public myclass (INT initialcount, string name)
{
Commonconstructor (initialcount, name );
}
Private void commonconstructor (INT count,
String name)
{
_ Coll = (count> 0 )?
New arraylist (count ):
New arraylist ();
_ Name = Name;
}
}
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 callthe 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:
// Not legal, using strates il generated:
Public myclass ()
{
Private arraylist _ Coll;
Private string _ name;
Public myclass ()
{
// Instance initializers wocould go here.
Object (); // not legal, legal strative only.
Commonconstructor (0 ,"");
}
Public myclass (INT initialcount)
{
// Instance initializers wocould go here.
Object (); // not legal, legal strative only.
Commonconstructor (initialcount ,"");
}
Public myclass (INT initialcount, string name)
{
// Instance initializers wocould go here.
Object (); // not legal, legal strative only.
Commonconstructor (initialcount, name );
}
Private void commonconstructor (INT count,
String name)
{
_ Coll = (count> 0 )?
New arraylist (count ):
New arraylist ();
_ Name = Name;
}
}
If you cocould write the Construction Code for the first version the way the compiler sees it, you 'd write this:
// Not legal, using strates il generated:
Public myclass ()
{
Private arraylist _ Coll;
Private string _ name;
Public myclass ()
{
// No variable initializers here.
// Call the third constructor, shown below.
This (0, ""); // not legal, legal strative only.
}
Public myclass (INT initialcount)
{
// No variable initializers here.
// Call the third constructor, shown below.
This (initialcount ,"");
}
Public myclass (INT initialcount, string name)
{
// Instance initializers wocould go here.
Object (); // not legal, legal strative only.
_ Counter = initialcount;
_ Name = Name;
}
}
The difference is that the compiler does not generate multiple callto 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.
Still don't buy the case for Constructor initializers? Then think about read-only constants. in this example, the name of the object shocould not change during its lifetime. this means that you should make it read-only. that causes the common utility function to generate Compiler Errors:
Public class myclass
{
// Collection of data
Private arraylist _ Coll;
// Number for this instance
Private int _ counter;
// Name of the instance:
Private readonly string _ name;
Public myclass ()
{
Commonconstructor (0 ,"");
}
Public myclass (INT initialcount)
{
Commonconstructor (initialcount ,"");
}
Public myclass (INT initialcount, string name)
{
Commonconstructor (initialcount, name );
}
Private void commonconstructor (INT count,
String name)
{
_ Coll = (count> 0 )?
New arraylist (count ):
New arraylist ();
// Error changing the name outside of a constructor.
_ Name = Name;
}
}
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.
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 shoshould understand both the order of operations and the default initialization of an object. you shoshould 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:
1. static variable storage is set to 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.
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.
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 callto other constructors, to minimize duplication.