The class instance constructor is a special method that allows you to initialize a type of instance to a good state.
The class instance constructor method is always called. ctor (constructor) in "method definition metadata table ). When creating an instance of the reference type, first allocate memory for the data field of the instance, and then initialize the additional fields of the object (type object pointer and synchronized block index ), finally, the instance constructor of the type is called to set the initialization status of the object.
When constructing an object of the reference type, the memory allocated to the object is always reset to zero before the instance constructor of the type is called. The constructor will ensure that all fields not explicitly overwritten have a 0 or null value. Unlike other methods, the instance constructor cannot be inherited. That is to say, the class only has the instance constructor defined by the class itself. If the class you define does not explicitly define Any constructor, the C # compiler defines a default (No parameter) constructor. In its implementation, it can only simply call the non-argument constructor of the base class.
piblic SomeType () :
A class can define multiple instance constructors. Each constructor must have a different signature and have different accessibility. To make the code "verifiable" (verifiable), the instance constructor of the class must call the constructor of the base class before accessing any fields inherited from the base class. In rare cases, you can create an instance of the type without calling the instance constructor. For example, the Object's MemberciseClone method. C # provides a simple syntax that allows initialization of fields defined in the type when constructing an instance of the reference type:
Internal Int32 m_x =
Let's take a look at the generated IL code, focusing on the code in the. ctor method! Obviously, the instance constructor saves the value 5 to the field m_x and then calls the constructor of the base class. That is to say, the C # compiler provides a simplified syntax that allows instance fields to be initialized in "inline" mode. The compiler helps us call the constructor method to execute initialization, but pay attention to the "Expansion effect" of the Code, which is defined as follows:
Int32 m_x = String m_s = Double m_d = SomeType() { SomeType(Int32 x) { SomeType(String s) { m_d =
Using the anti-compiler, we can see that there are three constructor. ctor methods. ==========
The struct constructor works in a different way than the referenced class constructor. CLR always allows creation of value-type instances, and there is no way to prevent value-type instantiation. Therefore, the value type does not need to define the constructor. The C # compiler does not generate a default non-argument constructor for the value type. Run the following code:
CLR allows you to define constructors for value types. However, the only way to execute this constructor is to write code to call them explicitly. As follows:
== = Point (, = Point (,
=m_y =
The correct answer is 0. Why? Because the Code does not explicitly call the Point constructor from any location, even if the value type provides a non-argument constructor. Obviously, the C # compiler reports an error: error CS0568: The structure cannot contain an explicit parameter-free constructor. C # The Compiler intentionally does not allow a value type with a non-argument constructor. This is designed to prevent developers from confused about when such constructor is called. No parameter constructor. The value type fields are always initialized to 0 or null. Strictly speaking, 0 or null is initialized only when fields of the value type are nested in the reference type. The Value Type field based on Stack is not guaranteed to be 0 or null. Note that although the C # compiler does not allow a value type with a non-argument constructor, the CLR does. You can use other languages (such as the IL assembly language) to define a value type with a non-argument constructor. Since C # does not allow the non-argument constructor to be defined for the value type, if the type is compiled, an error is returned.
Int32 m_x =
To generate "verifiable" code, all fields must be assigned a value before accessing any field of the value type. Therefore, all fields of the value type must be initialized by any constructor of the value type.
=
When compiling this code, the C # compiler reports an error, saying that the field "SomeValType. m_y" must be assigned a value. To solve this problem, you need to assign a value (usually 0) to y in the constructor ). The following is an alternative method for assigning values to all fields of the Value Type:
= m_x =}
In addition to the instance constructor, CLR also supports type constructor, also known as static constructor and class constructor.
The Type constructor can be used for interfaces (not allowed by C #), reference types, and value types. The instance constructor is used to set the initial status of an instance. The role of the Type constructor is to set the initial state of the type. By default, the Type constructor is not defined. If it is defined, only one can be defined. In addition, the Type constructor never has a parameter. For example:
Calling the Type constructor is troublesome. When compiling a method, the JIT compiler checks the types referenced in the code. If any type defines the Type constructor, the JIT compiler checks whether the Type constructor has been executed for the current AppDomain. CLR always makes sure that a Type constructor is executed only once. To ensure this, the calling thread needs to obtain a mutex thread synchronization lock when calling the Type constructor. In this way, if multiple threads attempt to call a Type constructor of a certain type, only one thread can obtain the lock, and other threads will be blocked. The first thread executes the code in the static constructor. When the first thread leaves the constructor, the waiting thread is awakened and the code in the constructor has been executed. Therefore, these threads will not execute code again and will be returned directly from the constructor method. In addition, if you call such a method again, the CLR knows that the Type constructor of the method has been executed, so that the constructor will not be called again. Although a Type constructor can be defined in the value type, do not do this because CLR sometimes does not call the Value Type constructor. The CLR ensures that a Type constructor is executed only once in each AppDomian, And the thread is secure during such execution, therefore, it is very suitable for initializing any Singleton object required by the Type constructor. The code in the Type constructor can only access static fields of the type, and its common purpose is to initialize these fields. Like the instance constructor, C # provides a simple primary class for initializing static fields. For example;
Int32 s_x =
Performance of Type initialization: when compiling a method, the JIT compiler determines whether to generate a call to the Type constructor in the method. If the JIT compiler decides to generate this call, it must also decide where to add this call. The following two possibilities are available: 1) the JIT compiler can be created just before the first instance of the type, or the call is generated just before a non-inherited field or member of the response class. This is called the "precise" (precise) syntax, because the CLR calls the Type constructor at the right time. 2) the JIT compiler may generate a time before accessing a static field or a static/instance method for the first time, or before calling an instance constructor. This is called the "before-field-init" Semantics before field initialization, because CLR only ensures that the Type constructor will run before accessing the members, and may be allowed early. "Field initialization before" Semantics is preferred, because it is the time when CLR can freely choose to call the Type constructor, and CLR will use this to generate code that runs faster as much as possible. By default, the language compiler selects the most appropriate semantics for the type you define, and sets the beforefieldinit identifier in the row of the type definition metadata table, to tell the CLR the choice. The following code focuses on how the C # compiler makes choices and how these choices affect the performance:
Int32 iterations = * * Int32 s_x = Precise() { s_x = = (Int32 x = ; x < iterations; x++ BeforeFieldInit.s_x = = (Int32 x = ; x < iterations; x++ Precise.s_x = = (Int32 x = ; x < iterations; x++= = (Int32 x = ; x < iterations; x++=
Result of running the above Code C # compiler if a class (BeforeFieldInit) contains a static field for inline initialization, A record with BeforeFieldInit metadata tag is generated in the Type Definition table of the class. C # If the compiler sees a class (Precise) containing an explicit Type constructor, it will not add the BeforeFieldInit metadata tag. Its basic principle is: static fields can be initialized only before access, and the specific time does not matter. The explicit Type constructor contains code that may have side effects, so you need to accurately take the running time.