(原创)c#学习笔记09--定义类03--构造函数和析构函数

9.3  构造函数和析构函数

  在C#中定义类时,常常不需要定义相关的构造函数和析构函数,因为在建立代码时,如果没有提供它们,编译器会自动添加它们。但是,如果需要,可以提供自己的构造函数和析构函数,以便初始化对象和清理对象。

  使用下述语法可以把一个简单的构造函数添加到类中:

class MyClass {
    public MyClass() {
        // Constructor code.
    }
}

  这个构造函数与包含它的类同名,且没有参数(使之成为类的默认构造函数),这是一个公共函数,所以类的对象可以使用这个构造函数进行实例化(详见第8章)。

  也可以使用私有的默认构造函数,即不能用这个构造函数来创建这个类的对象实例(它是不可创建的,详见第8章):

class MyClass {
    private MyClass() {
        // Constructor code.
    }
}

  最后,也可以用相同的方式给类添加非默认的构造函数,其方法是提供参数,例如:

class MyClass {
    public MyClass() {
        // Default constructor code.
    }
    
    public MyClass( int MyInt ) {
        // Nondefault constructor code ( uses myInt ).
    }
}

  可提供的构造函数的数量不受限制(当然不能耗尽内存,也不能有相同的参数集,所以“几乎无限制”更合适)。

  在.NET中使用的析构函数(由System.Object类提供)叫作Finalize(),但这不是我们用于声明析构函数的名称。使用下面的代码,而不是重写Finalize():

class MyClass {
    ~MyClass() {
        // Destructor body.
    }
}

  类的析构函数由带有~前缀的类名(与构造函数的相同)来声明。当进行垃圾回收时,就执行析构函数中的代码,释放资源。在调用这个析构函数后,还将隐式地调用基类的析构函数,包括System.Object 根类中的Finalize()调用。这个技术可以让.NET Framework 确保调用Finalize(),因为重写Finalize()是指基类调用需要显式地执行,这是具有潜在危险的(第10章将详细讨论如何调用基类的方法)。

构造函数的执行序列

  如果在类的构造函数中执行一个任务,把这些代码放在一个地方是非常方便的,这与第6章论述的把代码放在函数中有相同的优势。使用一个方法就可以把代码放在一个地方(详见第10章),而C#提供了一个更好的替代方式。任何构造函数都可以配置,在执行自己的代码前调用其他构造函数

  为了实例化派生的类,必须实例化它的基类。而要实例化这个基类,又必须实例化这个基类的基类,这样一直到实例化 System.Object(所有类的根)为止。结果是无论使用什么构造函数实例化一个类,总是要先调用System.Object.Object

  无论在派生类上使用什么构造函数(默认的构造函数或非默认的构造函数),除非明确指定,否则就使用基类的默认构造函数(稍后将介绍如何改变这个操作)。下面介绍一个简短示例,说明执行的顺序。考虑下面的对象层次结构:

public class MyBaseClass {
    public MyBaseClass() {
        
    }
    
    public MyBaseClass( int i ) {
        
    }
}

public class MyDerivedClass : MyBaseClass {
    public MyDerivedClass() {
        
    }
    
    public MyDerivedClass( int i ) {
        
    }
    
    public MyDerivedClass( int i, int j ) {
        
    }
}

  如果以下面的方式实例化MyDerivedClass:

MyDerivedClass myObj = new MyDerivedClass();

  则执行的顺序如下:

  执行System.Object.Object构造函数。 
  执行MyBaseClass.MyBaseClass()构造函数。 
  执行MyDerivedClass.MyDerivedClass()构造函数。

  另外,如果使用下面的语句:

MyDerivedClass myObj = new MyDerivedClass( 4 );

  则执行的顺序如下:

  执行System.Object.Object构造函数。 
  执行MyBaseClass.MyBaseClass()构造函数。 
  执行MyDerivedClass.MyDerivedClass(int i)构造函数。

  最后,如果使用下面的语句:

MyDerivedClass myObj = new MyDerivedClass( 4, 8 );

  则执行顺序如下:

  执行System.Object.Object构造函数。 
  执行MyBaseClass.MyBaseClass()构造函数。 
  执行MyDerivedClass.MyDerivedClass(int i, int j)构造函数。

  大多数情况下,这个系统会正常工作。但是,有时需要对发生的事件进行更多的控制。例如,在上面的实例化示例中,执行的顺序如下

  执行System.Object.Object构造函数。 
  执行MyBaseClass.MyBaseClass(int i)构造函数。 
  执行MyDerivedClass.MyDerivedClass(int i, int j)构造函数。 

  使用在个顺序可以把使用int i参数的代码放在MyBaseClass(int i)中, 即MyDerivedClass(int i, int j)构造函数要做的工作比较少,只需要处理int j参数(假定int i参数在两种情况下含义相同,虽然事情并非总是如此,但实际上我们常常做这样的安排)。只要愿意,C#就可以指定这种操作。

  为此,只需使用构造函数初始化器,它把代码放在方法定义的冒号后面。例如,可以在派生类的构造函数定义中指定所使用的基类构造函数,如下所示:

public class MyDerivedClass : MyBase {
    ...
    
    public MyDerivedClass( int i, int j ) : base( i ) {
        
    }
}

  其中, base关键字指定.NET实例化过程使用基类中有指定参数的构造函数。这里使用了一个int参数(其值通过参数i传送给MyDerivedClass构造函数),所以应使用MyBaseClass(int i)。这么做将不调用MyBaseClass(),而是执行本例前面列出的事件序列——也就是我们希望执行的事件序列。

  也可以使用这个关键字指定基类构造函数的字面值,例如,使用MyDerivedClass的默认构造函数调用MyBaseClass非默认的构造函数:

public class MyDerivedClass : MyBaseClass {
    public MyDerivedClass() : base( 5 ) {
        
    }
    
    ...
}

  这段代码将执行下述序列:

  执行System.Object.Object构造函数。 
  执行MyBaseClass.MyBaseClass(int i)构造函数。 
  执行MyDerivedClass.MyDerivedClass()构造函数。

  除了base关键字外,这里还可以将另一个关键字this用作构造函数初始化器。这个关键字指定在调用指定的构造函数前,.NET实例化过程对当前类使用非默认的构造函数。例如:

public class MyDerivedClass : MyBaseClass {
    public MyDerivedClass() : this( 5, 6 ) {
        
    }
    
    ...
    
    public MyDerivedClass( int i, int j ) : base( i ) {
        
    }
}

  这段代码将执行下述序列:

  执行System.Object.Object构造函数。 
  执行MyBaseClass.MyBaseClass(int i)构造函数。 
  执行MyDerivedClass.MyDerivedClass(int i, int j)构造函数。 
  执行MyDerivedClass.MyDerivedClass()构造函数。

  唯一的限制是使用构造函数初始化器只能指定一个构造函数。但是,如上一个示例所示,这并不是一个很严格的限制,因为我们仍可以构造相当复杂的执行序列。

  注意在定义构造函数时,不要创建无限循环。例如:

public class MyBaseClass {
    public MyBaseClass() : this( 5 ) {
        
    }
    
    public MyBaseClass( int i ) : this() {
        
    }
}

  使用上述任何一个构造函数,都需要先执行另一个构造函数,而另一个构造函数需要先执行原告的构造函数,因此这段代码可以编译,但如果尝试实例化 MyBaseClass,就会得到一个SystemOverflowException异常。

原文地址:https://www.cnblogs.com/wodehao0808/p/4955348.html