C#入门经典 - 9.3 构造函数和析构函数

在C#中定义类时,常常不需要定义相关的构造函数和析构函数,因为基类System.Object提供了一个默认的实现方式。但是,如果需要,可以提供自己的构造函数和析构函数,以便初始化对象和清理对象。

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

class MyClass

{

public MyClass()

}

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

也可以使用私有的默认构造函数,即这个类的对象实例不能用这个构造函数来创建:

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()是指基类调用需要显示地执行,这是有潜在危险的。

构造函数的执行序列

如果在类的构造函数中执行多个任务,把这些代码放在一个地方是非常方便的。使用一个方法就可以把代码放在一个地方,而C#提供了一个更好的方式。任何构造函数都可以进行配置,在执行自己的代码前调用其他构造函数。

在讨论构造函数前,先看看在默认情况下,创建类的实例时会发生什么情况。在开发过程中,对象常常并没有按照希望的那样执行,而是在调用构造函数时出现错误。这常常是因为类继承结构中的某个基类没有正确实例化,或者没有正确地给其类构造函数提供信息。理解在对象生命周期的这个阶段发生的事情,将更容易解决这类问题。

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

如果对一个类使用非默认的构造函数,默认的情况是调用基类的默认构造函数(这里与原书不一致。原书的内容是:"如果对一个类使用非默认的构造函数,默认的情况下是在其基类上使用匹配于这个构造函数签名的构造函数。如果没有找到这样的构造函数,就使用基类的默认构造函数(根类System.Object总是要使用默认的构造函数,因为这个类没有非默认的构造函数)。")。考虑下面的对象结构:

class MyBaseClass

{

public MyBaseClass()

{

Console.WriteLine("Call MyBaseClass().");

}

 

public MyBaseClass(int i)

{

Console.WriteLine("Call MyBaseClass(int i)");

}

}

 

class MyDerivedClass: MyBaseClass

{

public MyDerivedClass()

{

Console.WriteLine("Call MyDerivedClass().");

}

 

public MyDerivedClass(int i)

{

Console.WriteLine("Call MyDerivedClass(int i).");

}

 

public MyDerivedClass(int i, int j)

{

Console.WriteLine("Call MyDerivedClass(int i,int j)");

}

}

注:下面的内容与原书有差异,略有修改。我的代码是在.NET 3.5环境下运行。

如果以下面的方式实例化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()构造函数。
  • 执行MyDerivedClass.MyDerivedClass(int i,int j)构造函数。如图:

这个系统会正常工作,并确保派生类中的构造函数可以访问继承的成员。但是,有时需要对发生的事件进行更多的控制。例如,在上面的实例化实例中,事件的顺序如下:

  • 执行System.Object.Object()构造函数。
  • 执行MyDerivedClass(int i)构造函数。
  • 执行MyDerivedClass.MyDerivedClass(int i,int j)构造函数。如图:

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

为此,只需在派生类的构造函数定义中指定所使用的基类的构造函数,如下所示:

class MyDerivedClass: MyBaseClass

{

public MyDerivedClass()

{

Console.WriteLine("Call MyDerivedClass().");

}

 

public MyDerivedClass(int i)

{

Console.WriteLine("Call MyDerivedClass(int i).");

}

 

public MyDerivedClass(int i, int j):base(i)

{

Console.WriteLine("Call MyDerivedClass(int i,int j)");

}

}

其中,base关键字指定.NET实例化过程中使用基类中匹配指定签名的构造函数。

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

class MyDerivedClass: MyBaseClass

{

public MyDerivedClass():base(5)

{

Console.WriteLine("Call MyDerivedClass().");

}

 

public MyDerivedClass(int i)

{

Console.WriteLine("Call MyDerivedClass(int i).");

}

 

public MyDerivedClass(int i, int j):base(i)

{

Console.WriteLine("Call MyDerivedClass(int i,int j)");

}

}

MyDerivedClass myDerivedObject = new MyDerivedClass();

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

  • 执行System.Object.Object()构造函数。
  • 执行MyBaseClass.MyBaseClass(int i)构造函数。
  • 执行MyDerivedClass.MyDerivedClass()函数。如图:

 

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

class MyDerivedClass: MyBaseClass

{

public MyDerivedClass():this(5,6)

{

Console.WriteLine("Call MyDerivedClass().");

}

 

public MyDerivedClass(int i)

{

Console.WriteLine("Call MyDerivedClass(int i).");

}

 

public MyDerivedClass(int i, int j):base(i)

{

Console.WriteLine("Call MyDerivedClass(int i,int j)");

}

}

MyDerivedClass myDerivedObject = new MyDerivedClass();

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

  • 执行System.Object.Object()构造函数。
  • 执行The MyBaseClass.MyBaseClass(int i)构造函数。
  • 执行The MyDerivedClass.MyDerivedClass(int i, int j)构造函数。
  • 执行The MyDerivedClass.MyDerivedClass()构造函数。如图:

     

 

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

posted @ 2008-04-11 11:46  清香的工夫茶  阅读(1484)  评论(1编辑  收藏  举报