C # Sharp experience-Part 5: constructor and destructor)

Source: Internet
Author: User
5. constructor and destructor

Li Jianzhong (cornyfield@263.net), Nanjing University of Posts and Telecommunications)

Index


C # Sharp experience

"Hello, world! "Program

C # BASIC Language Introduction

Basic Structure of Microsoft. NET platform

Class and Object

Constructor and destructor

Method

Domain and attribute

Indexer and operator overload

Array and string

Features and ing

Com interoperability with unmanaged programming and Exception Handling

Use C # to weave the future -- C # programming model Overview

Constructor

The constructor initializes the member variables (fields) in the class. The C # class has two constructor types: instance constructor and static constructor. The instance constructor initializes the instance variables in the class. It is called only when the user allocates memory for the object with the new keyword. As a reference type class, the instantiated objects must be allocated to managed heap. The managed memory is managed by the. net clr runtime. Unlike C ++, objects in C # cannot be allocated to the stack. Users only declare objects and do not generate constructor calls.

The instance constructor can be divided into default constructor and non-default constructor. The default constructor is a non-parameter constructor that the compiler forces to add to the class without declaring any constructor, the constructor only calls the non-parameter constructor of the parent class. The default constructor is actually an additional rule adopted by the C # compiler to ensure that each class has at least one constructor. Note the following three points:

    1. The subclass does not declare any constructor;
    2. The default constructor added by the compiler as a subclass must be a non-parameter constructor;
    3. The parent class must have a non-parameter constructor.

Let's take a look at the output of the following example:

 
Using system; public class myclass1 {public myclass1 () {console. writeline ("myclass1parameterless contructor !");} Public myclass1 (string param1) {console. writeline ("myclass1constructor parameters:" + param1) ;}} public class myclass2: myclass1 {} public class test {public static void main () {myclass2 myobject1 = new myclass2 ();}}

CompileProgramRun the command to get the following output:

      Myclass1 parameterless contructor!

You can remove the non-argument constructor public myclass1 () of myclass1 to check the compilation result.

The constructor must pay special attention to inheritance. To ensure correct initialization of the parent class member variables, Any constructor of the Child class must call a constructor of the parent class by default, which constructor is called depends on the constructor's initialization parameter list. If the parameter list is not initialized, the sub-class constructor calls the non-parameter constructor of the parent class. If there is an initialization parameter list, this constructor of the subclass calls the parameter constructor corresponding to the parent class. Let's take a look at the output of the following example:

 
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 the program and run it to get the following output:

      Myclass1 constructor parameters: Hello
      Myclass2 constructor parameters: Hello

C # supports variable declaration initialization. The class member variable declaration Initialization is converted into a value assignment statement by the compiler and imposed on each constructor of the class. So what is the sequence between the initialization statement and the statement that calls the parent constructor? Let's take a look at the output of the following example:

 
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 the program and run it to get the following output:

      X = 1, y = 0
      X = 1, y =-1

It is easy to see that the initialization statement is the statement in the constructor before the parent class constructor calls it. That is to say, the priority of variable Initialization is the highest.

We can see that the class constructor Declaration has a public modifier, so of course there can also be a protected/private/internal modifier. According to the modifier rules, if we modify the constructor of a class to private, we cannot call the private constructor when inheriting the class, can't we inherit it? This is exactly the case. In fact, when all the member variables in our class are static, we do not want the class users to instantiate it, in this case, the constructors added by the compiler to us must be shielded (all constructors added by the compiler are public). Therefore, it is necessary to create a private instance constructor. Protected/internal has similar usage.

The constructor of the class has no return value, which is self-evident.

Static variables in the static constructor initialization class. The static constructor is not implicitly called in inheritance as the instance constructor, nor can it be called directly by users. The main point to master the static constructor is to master its execution time. The execution of the static constructor is not determined (the compiler is not clearly defined ). However, there are four rules to master:

    1. During the execution of a program, the static constructor can only execute once at most.
    2. The static constructor is executed after the static members of the class are initialized. Or the compiler will convert the static member initialization statement into a value assignment statement at the beginning of the execution of the static constructor.
    3. The static constructor is executed before the static members of any class are referenced.
    4. The static constructor is executed before the instance variables of any class are assigned.

Let's take a look at the output of the following example:

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 the program and run it to get the following output:

      Myclass1 static contructor
      Myclass1.method1
      Myclass2 static contructor
      Myclass2.method1

Of course, the output may also be:

      Myclass1 static contructor
      Myclass2 static contructor
      Myclass1.method1
      Myclass2.method1

It is worth noting that the instance constructor can reference instance variables or static variables. The static constructor can reference static variables. This is easy to understand in the semantics of classes and objects.
In fact, if we can deeply grasp the only purpose of the class constructor is to ensure that the member variables in the class can be correctly initialized, we have a friendly understanding of all kinds of constructors in C #-It has no reason not to do this!

Destructor

Because. NET platform's automatic garbage collection mechanism, C # class destructor are no longer as necessary as traditional C ++, the Destructor no longer takes on the memory release of the object members-the automatic garbage collection mechanism ensures memory recovery. In fact, there is no delete operation in C! The Destructor is only responsible for recycling and processing non-system resources. Typical examples include opening files, obtaining window handles, and database connections, network connections and other non-memory resources that need to be released by the user. Let's take a look at the output of the following example:

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 the program and run it to get the following output:

      Myclass2's destructor
      Myclass1's destructor

The last two sentences in the Program ensure that the class destructor is called. GC. Collect () is used to force the garbage collection thread to be started when the general language is running for garbage collection. GC. waitforpendingfinalizers () suspends the current thread and waits for the completion of the finalizaion operation. The finalizaion operation ensures that the class destructor are executed.

The Destructor will not be inherited. That is to say, the Destructor must be explicitly declared in the class before the class can exist. When you implement the destructor, the compiler automatically adds the Destructor that calls the parent class, which is described in the Finalize method below. The garbage collection mechanism is automatically called when appropriate. You cannot call the Destructor by yourself. Only the instance destructor, but not the static destructor.

How is the Destructor automatically called? This mechanism is supported by a finalizaion operation .. The default termination operation of the. NET system is not performed. If you need to release unmanaged resources, you only need to perform this operation in the Destructor-this is also recommended by C. Let's take a look at the following section.Code:

 
Using system; Class myclass1 {~ Myclass1 () {console. writleline ("myclass1 destructor ");}}

In fact, we can see from the generated intermediate code that the code is 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 users to overload or call the Finalize method themselves. The Compiler completely shields the Finalize method of the parent class (because of the single inheritance nature of C #, system. the object class is the ancestor class of all classes. Naturally, each class has a Finalize method. It seems that such a method does not exist at all. The following code is actually wrong:

Using system; Class myclass {override protected void finalize () {}// error public void mymethod () {This. Finalize (); // error }}

However, 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 is completely separated from the semantics of the "termination operation" and becomes a general method of the C # language. It is worth noting that this also shields the Finalize method of the parent class system. object, so be very careful!

There are many restrictions on termination operations in. Net runtime, which are often not recommended. After a finalizer is implemented for an object, the runtime adds the reference of this object to a queue called the terminated object reference set as a sign for termination. When garbage collection starts, if an object is no longer referenced, but it is added to the queue of the terminated object reference set, the object is not collected immediately at runtime, instead, this object is marked as an object requiring termination. After the garbage collection is complete, the terminating thread will be awakened during running to perform the terminating operation. Obviously, this will be removed from the linked list of the ending object reference set. This object starts to collect garbage and its memory resources are actually recycled only when the next garbage collection is performed. It is easy to see that the termination operation has made garbage collection twice, which will bring a lot of extra cost to the system. Termination is implemented by enabling the thread mechanism, which has a thread security issue .. . Net does not guarantee the sequence of terminated execution. That is to say, if object A has a reference pointing to object B, both objects have terminated operations, however, object A does not necessarily have a valid object a reference when terminating the operation .. Net does not allow users to directly call the finalize () method in the program running. If you urgently need such an operation, you can implement the idisposable interface to provide a public dispose () method. It must be noted that after the dispose () method is provided, you still need to provide the Finalize method operation, that is, to implement the pseudo-structure function. Because the dispose () method cannot be called. So. it is not recommended to terminate the object during the net runtime, that is, to provide the destructor. It is only necessary to strictly release unmanaged resources, such as database connections and file opening, this is required.

Most of the time, garbage collection should be controlled by. Net runtime, but sometimes it is necessary to manually control the garbage collection operation. For example, after a large-scale object set is operated, we are sure that we will not perform any operations on these objects any more, so we can force the garbage collection to be executed immediately, by calling system. GC. the collect () method can be implemented, but frequent collection significantly reduces system performance. In another case, an object has been placed on the chain of the terminated object reference set. However, if we have already terminated the object in some places in the program, that is, the dispose () method is explicitly called, and then the system can be called. GC. supressfinalize () is used to remove object references from the end object reference set chain to ignore the termination operation. The System Burden of terminating operations is very heavy.

I have learned more about it. net runtime automatic garbage collection, we will understand why the destructor in C # is so bent around to achieve our programming needs, in order to easily recycle memory resources and non-memory resources-this is the original structure!

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.