Nanjing University of Posts and Telecommunications Li Jianzhong (Cornyfield@263.net)
Constructor
The constructor is responsible for the initialization of member variables (domain). C # class has two constructors: example constructors and static constructors. The instance constructor is responsible for the instance variable in the initialization class, which is only called when the user uses the NEW keyword to assign memory. Moreover, as a class of reference types, the actually exemplarization object is inevitably assigned to manage the hinder. The hosted manager here means that the memory is managed by the CLR runtime of .NET. Unlike C , the object in the C # cannot be assigned in the stack, and the user only declares that the object does not generate the constructor.
Example constructors are divided into default constructors and non-default constructors. The default constructor is an unparalleled constructor that is added to the class without declaring any constructor, and the constructor only calls the non-parameter constructor of the parent class. The default constructor is actually a C # compiler to ensure additional rules taken by at least one constructor every class. Note the three points here:
Subclasses do not declare any constructors; the compiler is a default constructor having a subclass. It is certainly non-parametric constructor; the parent class must have a parameter-free constructor.
Look at the output of the example below:
Using system;
Public class myclass1
{
Public myclass1 ()
{
Console.writeline ("Myclass1
Parameterless contructor! ");
}
Public myclass1 (String param1)
{
Console.writeline ("Myclass1
Constructor parameters: " param1);
}
}
Public class myclass2: myclass1
{
}
Public Class Test
{
Public static void main ()
{
Myclass2 myObject1 = new myclass2 ();
}
}
Compile programs and runs to get the following output:
Myclass1 parameterless contructor!
The reader can remove the Non-Call-free constructor public myclass1 () to see the compile results.
The constructor requires special attention when inheriting, in order to ensure the correct initialization of the parent class member variable, any constructor of the subclass must call a certain constructor of the parent class, which constructor is to be used to see the initialization of the constructor. parameter list. If there is no initialization parameter list, then the constructor of the subclass calls the non-parameter constructor of the parent class; if there is a list of initialization parameters, the constructor of the subclass calls the parameter constructor corresponding to the parent class. Look at the output of the example below:
Using system;
Public class myclass1
{
Public myclass1 ()
{
Console.writeline ("Myclass1 Parameterless Contructor!");
}
Public myclass1 (String param1)
{
Console.writeline ("Myclass1
Constructor parameters: " param1);
}
}
Public class myclass2: myclass1
{
Public myclass2 (String param1): Base (param1)
{
Console.writeline ("MyClass2
Constructor parameters: " param1);
}
}
Public Class Test
{
Public static void main ()
{
Myclass2 myObject1 = new myclass2 ("hello");}
}
Compile programs and runs to get the following output:
Myclass1 Constructor Parameters: Hello
Myclass2 Constructionor Parameters: Hello
C # supports the declaration of the variable initialization. The member variable declaration within the class is initialized by the compiler into an assignment statement to impose the inside of each constructor in the class. So what is the order of initialization statements with the statement that calls the parent class constructor? Look at the output of the example below:
Using system;
Public class myclass1
{
Public myclass1 ()
{
PRINT ();
}
Public Virtual Void Print () {}
}
Public class myclass2: myclass1
{
INT x = 1;
Int Y;
Public myclass2 ()
{
Y = -1;
PRINT ();
}
Public Override Void Print ()
{
Console.writeline ("x = {0}, y = {1}", x, y);
}
}
Public Class Test
{
Static void main ()
{
Myclass2 myObject1 = new myclass2 ();
}
}
Compile programs and runs to get the following output:
X = 1, y = 0
X = 1, y = -1
Easy to see the initialization statement before the parent class constructor calls, the last executed is the statement in this constructor. In other words, the priority of the variable initialization is the highest.
We see a public modifier in the statement of the class constructor, and of course there can be protected / private / internal modifiers. According to the modifier rules, if we modify a class constructor as private, then when we inherit the class, we will not call this Private constructor. Will we inherit it? It is this. In fact, such a class in our class is still static (STIC), but does not want the user to instantiate it, and the compiler must be shielded to our secretizer (compiler) The added constructor is public), it is necessary to make a private instance constructor. Protected / Internal also has similar usage.
This constructor has no return value, which is not self-definition.
Static variable in the static constructor initialization. The static constructor is not invisible in the inheritance as an instance constructor, and cannot be called directly by the user. Mastering the static constructor is to master its execution time. The implementation of the static constructor is not determined (the compiler is not clearly defined). But there are four guidelines to be mastered:
During the execution of a program, the static constructor only executes up to once. Static constructors are executed after initialization of static members of the class. Or tell the compiler to convert a static member initialization statement into an assignment statement at the beginning of the static constructor. Static constructors are executed before any class of static members. The static constructor is executed before any class of instance variables.
Look at the output of the example below:
Using system;
Class myclass1
{
Static myclass1 ()
{
Console.writeline ("Myclass1 Static Contructor");
}
Public static void method1 ()
{
Console.writeLine ("Myclass1.Method1");
}
}
Class myclass2
{
Static myclass2 () {
Console.writeline ("Myclass2 Static Contructor);
}
Public static void method1 ()
{
Console.writeline ("Myclass2.Method1");
}
}
Class test
{
Static void main ()
{
Myclass1.method1 ();
Myclass2.method1 ();
}
}
Compile programs and runs to get the following output:
Myclass1 Static Contructor
Myclass1.method1
Myclass2 Static Contructor
Myclass2.method1
Of course, it is also possible to output:
Myclass1 Static Contructor
Myclass2 Static Contructor
Myclass1.method1
Myclass2.method1
It is worth noted that the instance constructor can reference the instance variable, or the static variable can also be referenced. The static variable can be referenced in the static constructor. This is easy to understand in the semantics of the class and objects. In fact, if we can deeply grasp the unique purpose of the structure of the class is to ensure that the members in the class can get the correct initialization, we have a good understanding of the various C # medium-shaped structures - it has no reason such!
Destructor
Due to the automatic garbage collection mechanism of the .NET platform, the destructor in the C # language is no longer as necessary, the destructor no longer assumes the memory release of the object member - the automatic garbage collection mechanism guarantees the recycling of memory. In fact, there is no DELETE operation in C #! The sectors are only responsible for recycling those non-system resources, relatively typical as: open files, obtained window handles, database connections, network connections, etc. need users to release non-memory resources. We look at the output of the example below:
Using system;
Class myclass1
{
~ Myclass1 ()
{
Console.writeline ("Myclass1's Destructor");
}
}
Class myclass2: myclass1
{
~ Myclass2 ()
{
Console.writeline ("Myclass2's Destructor");
}
}
Public Class Test
{
Public static void main ()
{
Myclass2 myObject = new myclass2 ();
MyObject = NULL;
Gc.collect ();
Gc.waitforpendingfinalizers ();
}
}
Compile programs and runs to get the following output:
Myclass2's Destructor
Myclass1's Destructor
The last two sentences in the program are to ensure that the parsers are called. Gc.collect () is to force the garbage collection thread to start the garbage collection thread when force the universal language. Gc.waitforpendingfinalizers () is the completion of the current thread waiting for the entire termination operation. Finalizaion operation guarantees the destructor of the class, which will be described in detail below.
The destructor will not be inherited, that is, the destructor must be clearly declared in the class. When the user implements the destructor, the compiler automatically adds a destructor that calls the parent class, which will be described in detail in the finalize method below. The destructor is automatically called because the garbage collection mechanism is automatically called when appropriate, and the user cannot call the destructor. Only the examples of the actuator do not have a static electors.
So how is the destructor being called automatically? This is supported by a .NET garbage collection mechanism from a Finalizaion operation. The .NET system default termination operation does not do any operation, if the user needs to release non-managed resources, the user can implement such an operation within the destructor - this is also a recommended approach. Let's look at the following code:
Using system;
Class myclass1
{
~ Myclass1 ()
{
Console.WritleLine ("Myclass1 Destructor");
}
}
In fact, from the generated intermediate code we can find that these codes are converted into the following code:
Using system;
Class myclass1
{
Protected Override Void Finalize ()
{
Try
{
Console.WritleLine ("My Class1 Destructor";
}
Finally
{
Base.Finalize ();
}
}
}
In fact, the C # compiler does not allow the user to overload or call the Finalize method - the compiler completely blocks the Finalize method of the parent class (due to the single inheritance of C #, the System.Object class is all kinds of ancestors, naturally each The classes have a Finalize method), which seems to have no similarity at all. We look at the code below is actually wrong:
Using system;
Class myclass
{
Override protected void finalize () {} // error
Public void mymethod ()
{
this.Finalize (); // error
}
}
But the following code is correct:
Using system;
Class myclass
{
Public void finalize ()
{
Console.writeline ("My Class Destructor");
}
}
Public Class Test
{
Public static void main ()
{
Myclass myObject = new myclass ();
MyObject.Finalize ();
}
}
In fact, the Finalize method here has been completely separated from the semantics of "termination operation", and a general method of C # language. It is worth noting that this also shields the Finalize method of the parent system.Object, so be careful!
Termination operations have many restrictions in .NET runtime, often do not be implemented. When a terminator is implemented for an object, the reference to this object will be added to a queue called the Terminal Reference Collection, as a logo that requires termination. When the garbage collection starts, if an object is no longer referenced but it has been added to the queue of the termination object reference set, then the object is not immediately garbage collection, but to terminate this object flag as required Operation object. After the garbage collection is completed, the termination thread will be delayed when the thread is running. Obviously, you should be deleted from the list of the termination object reference set. Only when the next garbage collection, this object began to real garbage collection, and the memory resource of the object was really recycled. It is easy to see that termination operations have made garbage collection twice, which will bring a small extra cost to the system. Termination is implemented by enabling a thread mechanism, which has a problem of thread security. .NET runtime does not guarantee the order in which termination execution, that is, if the object A has a reference to object B, two objects have termination operation, but the object A does not necessarily have a valid object when terminating operation. A reference. The .NET runtime does not allow the user to call the femalize () method directly in the program run. If the user urgently needs such an operation, Idisposable interfaces can be implemented to provide a common DISPOSE () method. It should be noted that the operation of the Finalize method still needs to be provided after providing the Dispose () method, that is, the destructive function of the hypotente is realized. Because the dispose () method does not guarantee being called. So .NET runtime is not recommended to provide a destructive function to provide a destructive function, just when there is a non-managed resource such as a database, the file is opened, and the file is required to be strictly released. Most of the time, garbage collection should be controlled by .NET runtime, but sometimes it is necessary to control the garbage collection operation. For example, after operating a large-scale object collection, we are confident that no longer operates on these objects, then we can force garbage collection immediately, which can be implemented by calling the system.gc.collect () method, But frequent collection will significantly reduce the performance of the system. There is also a situation that has already placed an object on the chain of the termination object reference set, but if we have done a termination operation in some places in the program, it is clearly called the Dispose () method, after that The reference to the object from the termination operation can be removed by calling System.gc.SupressFinalize () to ignore the termination operation. The system burden of termination operation is very heavy.
After understanding the automatic garbage collection feature of .Net runtime, we will understand why the destructor in C # has enabled our programming needs to achieve our programming needs, in order to make the memory resources and non-memory resources. Safe - this is also the original descent!