[C # advanced series] 07 method,
Instance construction and reference type
The previous chapter has actually written the construction process of the reference type:
First of all, in the heap, allocate memory for instance objects of the reference type, and then initialize additional fields of the object (that is, type object pointers and synchronized block indexes ).
At this time, the memory allocated to the object is directly set to 0. Therefore, if the constructors used do not process some fields in the object, the initial values of these fields should be 0 or null.
If a class does not have a constructor, a default no-argument constructor will be defined during the class construction, which simply calls the base class's no-argument constructor.
In rare cases, the instance type can be created without a practical instance constructor, such as the MemberwiseClone method (deep replication) and deserialization object. (Deserialization calls the GetUninitializedObject or GetSafeUninitializedObject method to allocate memory for the object. Let's talk about it later .)
Remember what I wrote in the previous chapter. inline initialization may cause performance problems:
Because each inline initialization will actually embed the operations on these initialization fields into the constructor code (note that these operations will be performed first before the real constructor operations ). If there is only one constructor function, it will not be affected. However, if there are multiple constructors, the code of this initialization field will be inserted in these constructors.
So when there are multiple constructor parameters and a lot of inline initialization in the code, there will be a lot of redundant code in the actually generated code. You can solve this problem by using the following methods:
Public class Troy {// do not perform inline initialization int _ a; int _ B; public Troy () {// put the initialization process in a construction parameter, this is generally in the non-argument constructor. _ a = 1; this. _ B = 2;} public Troy (int I): this () // when other constructors call this () {} public Troy (int I, int j ): this (){}}
Instance construction and Value Type
The value type does not need to be defined by the constructor, And the C # compiler does not embed the default non-argument constructor for the value type.
The Value Type nested in the reference type is initialized to 0 or null. If it is a stack-based value type, it is required to be forcibly initialized before reading. Otherwise, an error is reported.
Value-type instance constructor is only useful for explicit calls, otherwise its fields will be initialized to 0 or null. (That is to say, the real-time struct has no parameter constructor. As long as you do not display the call, no parameter constructor will be automatically called. In fact, the C # compiler does not allow you to write a non-argument constructor In the struct. After all, this point is too easy to misunderstand ).
Since C # does not allow the value type to define a non-argument constructor, the value type cannot be inline parameterized. (Static fields can be initialized inline because they are in type objects rather than instance objects)
During initialization, Any constructor of the value type must assign values to all fields of the value type.
Of course, there are solutions to this troublesome setup:
Public struct Troy {public int a; public int B; public Troy (int I) {this = new Troy (); // Initialize all fields as 0 or null. // initialize the desired field a = I ;}}
(I had to vomit. I just wrote a small example of value parameter initialization with VS, and deleted it by 360 as a virus .)
About the Type constructor
First, you must understand that the Type constructor is actually used to construct the type object in the CLR allocation memory during initialization.
The Type constructor does not allow parameters. Of course, only one Type constructor can be defined.
In fact, the Type constructor must be private and cannot even explicitly write the private modifier. This is exactly to prevent developers from calling it. The CLR is always responsible for its calls.
Simple Example:
Class Program {static void Main (string [] args) {Troy obj = new Troy () ;}} public class Troy {static Troy () {Console. writeLine ("I will ask you 6 or 6? ");}}
Construction Process:
When the JIT compiler compiles a method, it will check the types referenced by the Code. If any type defines the Type constructor, the JIT compiler checks whether the Type constructor is executed for the current AppDomain. No, no. Because CLR only needs to execute one Type constructor once in each AppDomain, in order not to make multiple threads call the Type constructor at the same time, when the first thread calls the Type constructor, gets a mutex thread synchronization lock. In this way, only one thread can be called. When the following thread is used, it is found that it has already been called, and the Type constructor will no longer be called. (Because the Type constructor thread is secure, it is suitable to initialize any singleton object in it .)
Although the Type constructor can be defined in the value type, in fact, because the value type does not have type objects in the heap, the code in it will not be called.
Operator Overloading
Actually, CLR does not know anything about Operator overloading, because it is a programming language syntax.
When an operator overload statement written in C # is compiled into the IL code, it has actually become a function with the specialname flag.
When the compiler sees the + operator, it will check whether the type of several operands has defined the function named op_Addition (the real function name after compilation ), the method parameter is compatible with the type of the operand.
Therefore, in the operator overload function, there must be a parameter of the same type as the one scheduled for this overload method:
public class Troy { public static int operator +(Troy a, Troy b) { return 10; } }
Conversion Operator Methods
Class Program {static void Main (string [] args) {Troy obj = 3; // the implicit conversion is successful string a = obj; // The result is a display conversion overload, therefore, this write method will not compile string a = (String) obj; // display conversion success} public class Troy {// implicit conversion operator implicit overload public static implicit operator Troy (Int32 num) {return new Troy ();} // The explicit conversion operator explicit reloads public static explicit operator String (Troy troy) {return "how to turn it into me ";}}
Like the normal +-operator overload, in the actual generated IL code, a new name is added with the prefix op _.
When the C # compiler detects that an object in the Code expects another object of different types, it will go back and find out whether the op_Implicit Method for implicit conversion is defined in the two types. Display similar.
You can refer to the definition of the Decimal class for understanding.
Extension methods
Let me talk about it first. In fact, I don't recommend using it.
Because the writing of nonstandard extension methods increases the difficulty of reading the code and the maintenance cost. (I'm sure some people will write this thing everywhere)
This item was previously mentioned in the study notes written in refactoring. It is mainly used to solve class libraries encapsulated by others and cannot add the desired functions.
19th points. The imperfect class libraries mentioned
To put it simply, use it with caution. Do not use extension methods for Classes written by yourself.
In addition, the extension method must be the static method defined in the top-level static class. If it is in a nested class, compilation will fail.
In fact, the Extension method is just a static function in a general static object after the C # compiler compilation, but adds the [Extension] feature. However, in fact, this ExtensionAttribute feature cannot be used in code. It is automatically generated by the C # compiler.
About the Division Method
The partial method is similar to the partial class, but the partial modifier is added before the method.
In this case, if other partial classes implement this method, this method will be added. If this method is not implemented, this code will be ignored during compilation.
However, the partial method can only be used in the partial class and structure, and the return type is always void. No parameter can be modified using out. The reason for this restriction is that methods may not exist at runtime, so there will be no returned results.
The partial method is always private, but the C # compiler prohibits explicit Writing of the private modifier before the partial method. (Similar to the Type constructor at this point)