9.13 析构函数
析构函数的作用是在类被销毁之前,对类实例使用的托管或非托管资源进行释放。通常,与运行时不进行垃圾回收的开发语言相比,C#无须太多的内存管理。这是因为.NET Framework垃圾回收器会隐式地管理对象的内存分配和释放。但是,当应用程序封装窗口、文件和网络连接这类非托管资源时,应当使用析构函数释放这些资源。
析构函数具有如下特点:
❑析构函数不能有访问修饰符;
❑析构函数不能有参数;
❑一个类只能有一个析构函数;
❑无法继承或重载析构函数;
❑无法调用析构函数;
❑无法预知析构函数何时被调用,因为它是被自动调用的。
接下来,看一个析构函数示例,如代码清单9-37所示。
代码清单9-37 析构函数
~Car()
{
System.Console.WriteLine(“析构函数调用了!”);
}
然后,看一下这个析构函数编译生成CIL后的结构,注意一点,析构函数编译成CIL后转化为了Finalize方法,如图9-12所示。
图 9-12 Car类的析构函数转换为了Finalize方法
代码清单9-38 是析构函数Finalize方法的CIL代码。
代码清单9-38 析构函数的CIL代码
.method family hidebysig virtual instance void
Finalize()cil managed
{
//Code size 25(0x19)
.maxstack 1
.try
{
IL_0000:nop
IL_0001:ldstr bytearray(90 67 84 67 FD 51 70 65 03 8C 28 75 86 4E 01 FF)
//.g.g.Qpe……(u.N……
IL_0006:call void[mscorlib]System.Console:WriteLine(string)
IL_000b:nop
IL_000c:nop
IL_000d:leave.s IL_0017
//end.try
finally
{
IL_000f:ldarg.0
IL_0010:call instance void[mscorlib]System.Object:Finalize()
IL_0015:nop
IL_0016:endfinally
}//end handler
IL_0017:nop
IL_0018:ret
}//end of method Car:Finalize
先从较高的视角观察代码清单9-38的整体结构,它使用了try-finally语句,在try块中获取并使用资源,并在finally块中释放资源,如下:
try{
//执行语句
}finally{
//这里的代码一定会被执行
}
这表示,析构函数中编写的代码被移到了try语句块中,而在finally语句块中添加了对基类的Finalize()方法的调用,这意味着将会对继承链中的所有实例递归地调用Finalize方法(从子类到基类)。
前面的析构函数代码被隐式地转换为以下代码:
protected override void Finalize()
{
try
{
//执行清理操作的语句
}
finally
{
base.Finalize();
}
}