1. instance constructor and class
Constructor is a special method that allows you to instantiate a type into a good state.
When creating an instance of the following type:
1) allocate memory for instance fields.
2) initialize additional fields of the object (type object pointer and synchronized block index ).
3) Call the instance constructor to set the initial state of the object.
When constructing a reference type object, before calling the instance constructor, the memory allocated to the object is always reset to zero first. The constructor does not explicitly override all fields to ensure that there is only one 0 or null value.
Unlike other methods, the instance constructor cannot be inherited.
Because the instance constructor cannot be inherited and the class only has the instance constructor defined by the class itself, virtual, new, override, sealed, and abstract modifiers cannot be used to define the constructor.
If the defined class does not explicitly define a constructor, the compiler will define a constructor without parameters by default. In the implementation of the default constructor, it simply calls the non-argument constructor of the base class.
Class Sometype
{
}
Class Sometype // These two definitions are equal.
{
Public Sometype ()
: Base ()
{
}
}
If the class modifier is abstract, the accessibility of the default constructor generated by the compiler is proctected.
If the base class does not provide a parameter-free constructor, the derived class must explicitly call a base class constructor.
Class Sometype
{
Public Sometype (int x)
{
}
Void DoSome ()
{}
}
Class Some: Sometype
{
Public Some () // compilation Error
{
}
Public Some (): base (5) // pass
{
}
}
If the modifier of the class is static, the compiler will not generate the default constructor in the class definition.
One type can define one or more instance constructors, each of which must have different signatures. In addition, it can have different accessibility.
Before accessing any fields inherited from the base class, the instance constructor of the class must first call the constructor of the base class. If the constructor of the derived class does not explicitly call a base class constructor, the compiler automatically generates a call to the default base class constructor. In the end, the System. Object public parameter-free constructor will be called. This constructor does not work and will return directly. Because System. Object does not define the instance data field, its constructor has nothing to do.
In rare cases, you can create an instance of the type without calling the base class constructor. A typical example is the MemberwiseClone method of the Object. This method is used to allocate memory and initialize additional fields of the object (you should remember, type object pointers and synchronized block indexes)
Then, copy the bytes of the source object to the new object. In addition, when the runtime seializer deserializes an object, it usually does not need to call the constructor. The deserialization Code uses the GetUnintializedObject or GetSafeuninitializedObject method of the System. Runtime. Serialization. FormatterServices type to allocate memory to the object. No constructor is called during this period.
Bingo (do not call any virtual methods that will affect the constructed object in the constructor, because if this virtual method is overwritten in the derived type of the type to be instantiated, the rewriting implementation will be called, but in the inheritance hierarchy, the field has not been fully initialized (the constructors of the derived type have not yet called ).)
Let's see how field initialization occurs in the following example.
Sealed class Sometype
{
Private Int32 x = 5; // The sometype constructor stores 5 in field x, and then calls the base class constructor. C # The Compiler provides a simplified syntax. Allows you to initialize instance fields in the internal connection mode. But behind the scenes
// It converts the syntax into the code in the constructor to execute initialization.
}
Sealed class Some
{
Private Int32 x = 5;
Private string s = "there ";
Private Int32 I;
Public Some ()
{
} // When the compiler generates code for the three constructors, the code used to initialize x, s, and I is included at the beginning of each method. In these
// After code initialization, the compiler inserts a call to the base class constructor and inserts its own constructor code.
// Here, even if I is not explicitly initialized, the I will be initialized to 0.
Public Some (int x1) {x = 10 ;}
Public Some (string s1) {s = s1 ;}
}
In the above example, some classes have three constructors, so the compiler generates 3 code for initializing x, s, and I. If there are several initialized instance fields and a large number of overloaded constructor methods, consider the following method:
Sealed class Some
{
Private Int32 x;
Private string s;
Private Int32 I;
// This constructor sets all fields as default values.
// All other constructor displays that the constructor is called.
Public Some ()
{
X = 30;
S = "there ";
I = 300;
}
// This constructor sets all fields as default values and then modifies the default values of x.
Public Some (int x1)
: This ()
{
X = x1;
}
// Same as above...
Public Some (string s1)
: This ()
{
S = s1;
}
}
2. Structure of the Instance Constructor (value type)
The struct constructor works differently from the reference type. CLR allows you to create value-type instances and cannot block Value Type instantiation. Therefore, the value type does not require a constructor, And the compiler does not generate a default constructor for the value type.
However, CLR allows a value type definition constructor, but if you want to execute the defined constructor, you must explicitly call them.
Struct Point
{
Private int x, y;
Public Point (int x1, int y1) // If the constructor is explicitly declared, values must be assigned to all fields of the value type. This is because to generate "verifiable" code, assign values to all fields before accessing any field of the value type.
{
X = x1;
Y = y1;
}
Public Point () // The struct cannot contain an explicit non-argument constructor. An error occurs during compilation!
{// In fact, even if the value type is provided, many compilers will not generate code to automatically call it. In order to execute the value type's no-argument constructor, the developer must explicitly call the Value Type constructor.
X = 20; y = 30;
} // C # The Compiler intentionally does not allow the value type to define a non-argument constructor. This is to prevent developers from confused about when the constructor will be called, because the non-argument constructor cannot be defined, therefore, the compiler will never generate code that automatically calls it.
}
Sealed class Rectangle
{
Private Point p1, p2; // The value-type instance constructor is executed only when it is explicitly called,
Public Rectangle ()
{
P1 = new Point (100, 20); // if the Point constructor is not called using the new operator, the field x and y of the Point will be 0; here, x = 100 of p1, y = 200. all p2 values are 0.
}
}
When a value type has many fields, it will be difficult to assign values one by one .. The following is an alternative method for assigning values to all fields of the Value Type:
Struct Point
{
Public int x, y, z, q, n, m;
Public Some some;
Public Point (int x1, int y1) // a constructor with parameters can be defined for the value type.
{
// The Field initializes all fields to 0/null.
This = new Point ();
X = x1; // use the value of x1 to overwrite 0 of x;
Y = y1; // The value of y1 overwrites 0 of y;
}
}
Class Program
{
Public const Program p = null;
Static void Main ()
{
Point p = new Point (1, 200,100 );
Some som = p. some; // here som is null
Int x = p. x; // 200
Int y = p. y; // 100
Int z = p. z; // 0
Console. ReadKey ();
}
}
In the value type, this indicates an instance of the value type. an instance of the new type can be assigned to this. In the new process, all fields are 0/null. In the reference type, this is considered read-only and cannot be assigned a value.
3. Type constructor
That is, the static constructor, class constructor, or type initializer. The Type constructor can be used to reference types and value types for interfaces (C # compiler is not allowed, CLR is allowed.
The instance constructor is used to set the initial status of an instance. Correspondingly, the Type constructor is used to set the initial state of the type. No Type constructor is defined by default. If you define it yourself, you can define only one. The Type constructor never has a parameter!
Sealed class Some
{
Static Some () // you can see that the Type constructor is similar to the instance constructor. The difference is that they must be marked as static.
// The Type constructor does not access your modifier and is always private. However, if you explicitly mark the type as private, the compiler will show that the static constructor does not allow access modifiers.
// The reason for private is to prevent any code written by developers from calling it. the CLR is always responsible for calling the Type constructor.
{
}
}
Calling the Type constructor is troublesome. When compiling a method, the JIT compiler will view the types referenced in the code. For any type that defines the Type constructor, the JIT compiler checks whether the constructor of this type has been executed for the current AppDomain. If the constructor has never been executed, the JIT compiler adds a call to the Type constructor in its native code. If the Type constructor has been executed, the JIT compiler does not add a call to it, because the JIT compiler knows that the type has been initialized.
Now, after the method is compiled by JIT, the thread starts to execute it and finally runs the code of the type of constructor called. In fact, multiple threads may execute the same method at the same time. CLR wants to ensure that one Type constructor can only be executed once in each AppDomian (application domain). To ensure this, when calling the Type constructor, the call thread needs to obtain a mutex synchronization lock. In this way, at a certain time, only one thread will execute the code in the Type constructor. The first thread will execute the code in the Type constructor, and the first thread will release the lock, when the first thread leaves the constructor, the waiting thread will be awakened and the code of the constructor will not be executed if it finds that the code of the constructor has been executed, return directly from the constructor method.
The code in the Type constructor can only access static fields of the type. And its general purpose is to initialize these fields. Similar to the instance field above, C # also provides a simple syntax to initialize static fields of the type.
sealed class Rectangle
{
private static int i = 200;
}
When an appeal code is generated, the compiler automatically generates a Type constructor with the code equal:
sealed class Rectangle
{
private static int i;
static Rectangle()
{
i = 200;
}
}
The Type constructor should not call the Type constructor of the base type, because the type cannot have static fields that are shared or inherited from the base type.
Although the Type constructor can be defined in the value type, never do this:
Struct Point
{
Static Point ()
{
Console. WriteLine ("This statement will never be displayed"); // because CLR will not call a static constructor of the value type.
}
}
Type constructor performance:
Class Program
{
Static void Main ()
{
PerfTest1 (1000000000 );
PerfTest2 (1000000000 );
Console. ReadKey ();
}
// When this method is compiled by jit, The BeforeFieldInit and Precise class type constructors are not executed yet, so calls to these constructors will be embedded in the code of this method, make it run slowly.
Private static void PerfTest1 (int iterations)
{
Stopwatch sw = Stopwatch. StartNew ();
For (int x = 0; x <iterations; x ++)
{
// The jit compiler optimizes the code that calls the BeforeFileldInit Type constructor so that it can be executed before the loop.
BeforefieldInit. s_x = 1;
}
Console. WriteLine ("PerfTest1: {0} BeforeFileldInit", sw. Elapsed );
Sw = Stopwatch. StartNew ();
For (int x = 0; x <iterations; x ++)
{
// The jit compiler generates code for calling the Precise class Type constructor here. Therefore, you must check whether the constructor needs to be called once every iteration.
Precise. s_x = 1;
}
Console. WriteLine ("PerfTest1: {0} Precise", sw. Elapsed );
}
// When this method is compiled by jit, The BeforeFieldInit and Precise class type constructors have been executed, so the method code will not generate calls to these constructors, it runs faster.
Private static void PerfTest2 (int iterations)
{
Stopwatch sw = Stopwatch. StartNew ();
For (int x = 0; x <iterations; x ++)
{
BeforefieldInit. s_x = 1;
}
Console. WriteLine ("PerfTest2: {0} BeforeFileldInit", sw. Elapsed );
Sw = Stopwatch. StartNew ();
For (int x = 0; x <iterations; x ++)
{
Precise. s_x = 1;
}
Console. WriteLine ("PerfTest2: {0} Precise", sw. Elapsed );
}
}
// Because this class does not display the constructor of the definition type #
// Use BeforeFieldInit (before field initialization) in metadata to mark and define
Internal sealed class BeforefieldInit
{
Public static int s_x = 123;
}
// Because the class explicitly defines the Type constructor, BeforefieldInit is not used to mark the definition
Internal sealed class Precise
{
Public static int s_x;
Static Precise ()
{
S_x = 123;
}
}
C # If the compiler sees a class (BeforeFieldInit) containing a static field for inline initialization, it will generate a record item in the Type Definition table that has added the BeforeFieldInit metadata tag,
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. It does not matter when it is accessed. The explicit Type constructor may
Contains code with side effects. Therefore, you need to precisely take the running time.
From the output, this decision has a great impact on performance.