using System;
namespace usingDemo
{
public class FinalizeDisposeBase : IDisposable
{
// 标记对象是否已被释放
private bool _disposed = false;
// Finalize方法
~FinalizeDisposeBase()
{
Dispose(false);
}
/// <summary>
/// 这里实现了IDisposable中的Dispose方法
/// </summary>
public void Dispose()
{
Dispose(true);
// 告诉GC此对象的Finalize方法不再需要调用
GC.SuppressFinalize(true);
}
/// <summary>
/// 在这里做实际的析构工作
/// 声明为虚方法以供子类在必要时重写
/// </summary>
/// <param name="isDisposing"></param>
protected virtual void Dispose(bool isDisposing)
{
// 当对象已经被析构时,不在执行
if(_disposed)
{
return;
}
if(isDisposing)
{
// 在这里释放托管资源
// 只在用户调用Dispose方法时执行
}
// 在这里释放非托管资源
// 标记对象已被释放
_disposed = true;
}
}
public sealed class FinalizeDispose:FinalizeDisposeBase
{
private bool _mydisposed = false;
protected override void Dispose(bool isDisposing)
{
// 保证只释放一次
if (_mydisposed)
{
return;
}
if(isDisposing)
{
// 在这里释放托管的并且在这个类型中声明的资源
}
// 在这里释放非托管的并且在这个类型中声明的资源
// 调用父类的Dispose方法来释放父类中的资源
base.Dispose(isDisposing);
// 设置子类的标记
_mydisposed = true;
}
static void Main()
{
}
}
}
上面的代码是一个近乎完美的Dispose配合Finalize的设计模板,其中有几点需要特别注意:
真正做释放工作的只是Virtual的受保护方法Dispose方法,事实上这个方法的名字并不重要,仅仅为了通用和更好理解,称呼它为Dispose。
虚方法Dispose需要接受一个布尔类型的参数,主要用于区分调用方是类型的使用者还是.NET的垃圾回收。前者通过IDisposable的Dispose方法,而后者通过Finalize方法。 两者的区别是通过Finalize方法释放资源时不能再释放或使用对象中的托管资源,这是因为这时的对象已经处于不被使用的状态,很有可能其中的托管资源已经被释放掉了。
在IDisposable的Dispose方法的实现中通过GC.SuppressFinalize()方法来告诉.NET此对象在被回收时不需要调用Finalize方法,这一句是改善性能的关键,记住实现Dispose方法的本质目的就是避免所有释放工作在Finalize方法中进行。
子类型必须定义自己的释放标记来标明子类中的资源是否已经被释放,同时子类的虚方法Dispose方法也只需要释放自己新定义的资源。
确保在虚方法Dispose中做的都是释放工作,有些逻辑上的结束工作需要反复斟酌,以防止一个简单的赋值语句使对象再度存活。