public class Class1 : IDisposable { public Class1() { } ~Class1 () { //垃圾回收器将调用该方法,因此参数需要为false。 Dispose (false); } //该方法定义在IDisposable接口中。 public void Dispose () { //该方法由程序调用,在调用该方法之后对象将被终结。 //因为我们不希望垃圾回收器再次终结对象,因此需要从终结列表中去除该对象。 GC.SuppressFinalize (this); //因为是由程序调用该方法的,因此参数为true。 Dispose (true); } //所有与回收相关的工作都由该方法完成 private void Dispose(bool disposing) lock(this) //避免产生线程错误。 { if (disposing) { //需要程序员完成释放对象占用的资源。 } //对象将被垃圾回收器终结。在这里添加其它和清除对象相关的代码。 } } } |
我们先来谈谈析构函数。
析构函数是不可继承的。因此,除了自已所声明的析构函数外,一个类不具有其他析构函数。
由于析构函数要求不能带有参数,因此它不能被重载,所以一个类至多只能有一个析构函数。
析构函数是自动调用的,它不能被显式调用。当任何代码都不再可能使用一个实例时,该实例就符合被销毁的条件。此后,它所对应的实例析构函数随时均可能被调用。销毁一个实例时,按照从派生程度最大到派生程度最小的顺序,调用该实例的继承链中的各个析构函数。析构函数可以在任何线程上执行。
下列示例的输出
using System;
class A
{
~A() {
Console.WriteLine("A's destructor");
}
}
class B: A
{
~B() {
Console.WriteLine("B's destructor");
}
}
class Test
{
static void Main() {
B b = new B();
b = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
为
B's destructor
A's destructor
这是由于继承链中的析构函数是按照从派生程度最大到派生程度最小的顺序调用的。
析构函数实际上是重写了 System.Object
中的虚方法 Finalize
。C# 程序中不允许重写此方法或直接调用它(或它的重写)。例如,下列程序
class A
{
override protected void Finalize() {} // error
public void F() {
this.Finalize(); // error
}
}
包含两个错误。
编译器的行为就像此方法和它的重写根本不存在一样。因此,以下程序:
class A
{
void Finalize() {} // permitted
}
是有效的,所声明的方法隐藏了 System.Object
的 Finalize
方法。
好,现在我们开始来谈谈Finalize 和Dispose。
Finalize 和Dispose(bool disposing)和 Dispose() 的相同点:
这三者都是为了释放非托管资源服务的.
Finalize 和 Dispose() 和Dispose(bool disposing)的不同点:
这个时候我们可能比较疑惑,为什么还需要一个Dispose()方法?难道只有一个Dispose(bool disposing)或者只有一个Dispose()不可以吗?
答案是:
只有一个Dispose()不可以. 为什么呢?因为如果只有一个Dispose()而没有Dispose(bool disposing)方法.那么在处理实现非托管资源释放的代码中无法判断该方法是客户调用的还是垃圾回收器通过Finalize调用的.无法实现判断如果是客户手动调用,那么就不希望垃圾回收器再调用Finalize()(调用GC.SupperFinalize方法).另一个可能的原因(:我们知道如果是垃圾回收器通过Finalize调用的,那么在释放代码中我们可能还会引用其他一些托管对象,而此时这些托管对象可能已经被垃圾回收了, 这样会导致无法预知的执行结果(千万不要在Finalize中引用其他的托管对象).
所以确实需要一个bool disposing参数, 但是如果只有一个Dispose(bool disposing),那么对于客户来说,就有一个很滑稽要求,Dispose(false)已经被Finalize使用了,必须要求客户以Dispose(true)方式调用,但是谁又能保证客户不会以Dispose(false)方式调用呢?所以这里采用了一中设计模式:重载 把Dispose(bool disposing)实现为 protected, 而Dispose()实现为Public,那么这样就保证了客户只能调用Dispose()(内部调用Dispose(true)//说明是客户的直接调用),客户无法调用Dispose(bool disposing).
范例如下:
public class BaseResource: IDisposable
{
//前面我们说了析构函数实际上是重写了 System.Object
中的虚方法 Finalize
, 默认情况下,一个类是没有析构函数的,也就是说,对象被垃圾回收时不会被调用Finalize方法
~BaseResource()
{
// 为了保持代码的可读性性和可维护性,千万不要在这里写释放非托管资源的代码
// 必须以Dispose(false)方式调用,以false告诉Dispose(bool disposing)函数是从垃圾回收器在调用Finalize时调用的
Dispose(false);
}
// 无法被客户直接调用
// 如果 disposing 是 true, 那么这个方法是被客户直接调用的,那么托管的,和非托管的资源都可以释放
// 如果 disposing 是 false, 那么函数是从垃圾回收器在调用Finalize时调用的,此时不应当引用其他托管对象所以,只能释放非托管资源
protected virtual void Dispose(bool disposing)
{
// 那么这个方法是被客户直接调用的,那么托管的,和非托管的资源都可以释放
if(disposing)
{
// 释放 托管资源
OtherManagedObject.Dispose();
}
//释放非托管资源
DoUnManagedObjectDispose();
// 那么这个方法是被客户直接调用的,告诉垃圾回收器从Finalization队列中清除自己,从而阻止垃圾回收器调用Finalize方法.
if(disposing)
GC.SuppressFinalize(this);
}
//可以被客户直接调用
public void Dispose()
{
//必须以Dispose(true)方式调用,以true告诉Dispose(bool disposing)函数是被客户直接调用的
Dispose(true);
}
}
上面的范例达到的目的:
1/ 如果客户没有调用Dispose(),未能及时释放托管和非托管资源,那么在垃圾回收时,还有机会执行Finalize(),释放非托管资源,但是造成了非托管资源的未及时释放的空闲浪费
2/ 如果客户调用了Dispose(),就能及时释放了托管和非托管资源,那么该对象被垃圾回收时,不回执行Finalize(),提高了非托管资源的使用效率并提升了系统性能