在说IDisposable
有必要了解一个Object.Finalize
方法。
Object.Finalize 方法
C#中每个类都有一个隐藏的protected方法Finalize,如果程序中没有显式实现,编译器会自动为其实现一个。
Finalize 方法用于在销毁对象之前对当前对象占用的非托管资源执行清理操作。——MSDN
怎样实现Finalize?
在实际编程中,直接实现Finalize方法,会提示"引入Finalize方法会妨碍析构函数调用。是否希望声明析构函数?"。
所以一般是实现析构函数取而代之,而编译器不允许同时实现析构函数和Finalize方法。
程序中实现了析构函数,编译器会根据析构函数的代码生成相应的Finalize,并在其末尾自动调用父类的Finalize方法,最终Finalize方法由GC在回收资源时调用。
实现析构函数就等于实现了Finalize。或者简单一点说:析构函数就等于Finalize方法。
在默认实现的Finalize方法中,子类的Finalize方法会递归调用父类的Finalize方法,所以猜测(未证实)这也是不建议直接实现Finalize方法的原因。
IDisposable接口
提供一种用于释放非托管资源的机制。——MSDN
IDisposable接口和Finalize方法一样,都是为了释放非托管资源。区别在于:
- Finalize释放由GC自动执行,Dispose释放是手动执行,执行的时机不一样,Dispose可以及时回收不再使用的资源。
- Finalize释放自身的非托管资源,不包括其所包含的托管对象中的非托管资源;Dispose释放的是托管(托管对象中的非托管资源)和非托管资源。
Finalize不必显式释放托管资源,一个对象中托管资源如果后续再无引用,GC自然会将其通过Finalize收回;如果仍需引用,释放反而造成错误。
IDisposable接口实现
基于上述约定,很容易想到如下实现:
class A : IDisposable
{
public void Dispose()
{
// 释放托管资源
ReleaseManaged();
// 释放非托管资源
ReleaseUnmanaged();
}
~A()
{
ReleaseUnmanaged();
}
private void ReleaseManaged()
{
}
// 提取析构函数和Dispose公共的部分
private void ReleaseUnmanaged()
{
}
}
MSDN给出的模式是实现一个protected void Dispose(bool disposing)
方法。
- disposing为true,表示手动释放,此时应释放所有资源;
- disposing为false,只需释放非托管资源;
- protected允许子类调用释放资源。
public class BaseClass : IDisposable
{
private bool disposed = false;
// The class constructor.
public BaseClass()
{
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!this.disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if (disposing)
{
// Dispose managed resources.
}
//Dispose managed resources.
// ...
// Note disposing has been done.
disposed = true;
}
}
~BaseClass()
{
Dispose(false);
}
}
说明:
- 为了防止重复释放资源,使用一个bool成员disposed表示资源是否已被释放。
- Dispose()中额外调用
GC.SuppressFinalize(this)
,通知GC不必在调用Finalize,因为此时所有资源已被释放。 - Dispose()中使用Dispose(true)释放资源;析构函数使用Dispose(false)释放资源。
BaseClass
作为基类,必须完整实现IDisposable和Finalize方法;它的派生类,如果包含非托管资源,应重写 Dispose(Boolean) 并调用基类 Dispose(Boolean) 实现资源释放:
class DerivedClass : BaseClass
{
bool disposed = false;
SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);
protected override void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing) {
handle.Dispose();
}
disposed = true;
base.Dispose(disposing);
}
}
派生类可以不实现析构函数,语言的多态性使得在GC自动回收时会调用Dispose(false)释放资源。