- 利用运行库强制执行的析构函数,但析构函数的执行是不确定的,而且,由于垃圾回收的工作方式,它会给运行库增加不可接受的系统开销
- IDisposable接口提供了一种机制,该机制允许类的用户控制释放资源的时间,但需要保证调用Dispose()方法
建议:如果实现了析构函数,那么就应该实现IDisposable接口。
假定大多数程序员都能正确调用Dispose()方法,同时把实现析构函数作为一种安全机制,以防止没有调用Dispose()方法。以下是一个双重实现的样例:
pulibc class ResourceHolder : System.IDisposable
{
private bool _isDisposed = false;
public void Dispose()
{
this.Dispose(true);
// 该方法告诉垃圾回收器有一个类不再需要调用其析构函数了
// 因为Dispose()方法已经完成了所有需要清理的工作,所以析构函数不需要做任何工作
// 调用SuppressFinalize()方法就意味着垃圾会后期认为这个对象根本没有析构函数
System.GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
// 使用这种结构的原因是,当一个对象被释放时,它所占用的资源必须被全部释放
// 释放既可以通过代码实现,也可以通过DotNet垃圾回收机制来完成
// 因为垃圾回收机制只能释放托管的资源,因此非托管资源必须通过代码进行释放
// 首先需要判断该对象有没有被释放,在没有被释放的情况下才进行释放
if(!this._isDisposed)
{
// 判断是否执行托管资源的释放
// 垃圾回收机制认为每个类所占有的资源由类本身进行释放,
// 如果一个类中包含另一个类对象的引用,该引用对象所占有的资源的释放由该引用对象自身来保证
// 因此,正常情况下都是在析构函数中调用 Dispose(false)
if(disposing)
{
// Cleanup managed objects by call their Dispose() methods
}
// Cleanup unmanaged objects
}
}
// 析构函数
~ResourceHolder()
{
this.Dispose(false);
}
}
由于垃圾回收机制不会去释放非托管资源,而析构函数是垃圾回收机制自动调用的。因此,必须在析构函数中释放对象的非托管资源,对象所涉及的托管资源留给运行时处理。用户调用Dispose()方法时,是已经明确要释放该对象的所有资源(包含托管资源与非托管资源)