能在.Net Framework中找到的类产生的对象,都是 托管资源。自定义的类型需要注意是否用到了非托管资源。
非托管资源 则是CLR无法对这些资源管理,这些资源的申请、释放必须由使用者自行管理。例如,像Win32 API编程中的文件句柄,上下文句柄、窗口、GDI+对象、COM对象、数据库连接或网络连接等(P/Invoke)资源、StreamReader等各种流都属于非托管资源。
资源释放,一般指的是非托管资源释放,托管资源由GC自动管理。
终结器 | 应用 | 备注 |
---|---|---|
~ClassName(){} | 必须 | 确保释放非托管资源,GC会隐式调用 |
Finalize(){} | 尽量不用 | 很像C++的析构函数 |
在Finalize方法中应该尽量避免引用其他实现了Finalize方法的对象,减少依赖性问题。
一个实现了Finalize方法的对象必需等两次GC才能被完全释放,可见,这种“自动”释放资源的方法并不能满足我们的需要,因为我们不能显示的调用它(只能由GC调用),而且会产生依赖型问题。我们需要更准确的控制资源的释放。
以此代码中最好避免使用终结器,也应尽量少让代码的逻辑使用到终结器。
Dispose是提供给我们显示调用的方法。使用using()调用,能自动调用对象Dispose方法。Dispose一般实现模式如下:
IDisposiable是显示释放对象的接口,实现IDisposiable接口的类,可以显式地释放对象。
/// <summary>
/// 含有非托管资源的类,继承Dispose
/// </summary>
public class DisposePattern : IDisposable
{
// 是否释放,true已释放
private bool _isDisposed = false;
// 锁对象
private static readonly object _clockObj = new object();
// 非托管资源
private System.IO.FileStream fs = new System.IO.FileStream("test.txt", System.IO.FileMode.Create);
// 托管资源
private List<string> data = new List<string>();
// 包含非托管资源的类才有意义,防止使用者没有显示调用Dispose方法
~DisposePattern()
{
// false时,只处理非托管资源即可,托管资源会自动清理
Dispose(false);
}
// 显示调用
public void Dispose()
{
// true时要同时处理托管资源、非托管资源
Dispose(true);
//告诉GC不需要再调用Finalize方法,
//因为资源已经被显示清理
GC.SuppressFinalize(this);
}
// 实际释放资源的地方
// 受保护的虚方法,考虑到这个类型被继承时,子类也许会实现自己的Disponse。这时子类要调用base.Disponse。
protected virtual void Dispose(bool disposing)
{
//不需要多次释放
if (_isDisposed) return;
//由于Dispose方法可能被多线程调用,所以加锁以确保线程安全
lock (_clockObj)
{
if (disposing)
{
//说明对象的Finalize方法并没有被执行,在这里可以安全的引用其他实现了Finalize方法的对象
//释放托管资源,包括事件监听
if (data != null)
{
data.Clear();
data = null;
}
}
//释放非托管资源
if (fs != null)
{
fs.Dispose();
fs = null; //标识资源已经清理,避免多次释放
}
// 让类型知道自己已经被释放
_isDisposed = true;
}
}
}
/// <summary>
/// 含有非托管资源的派生类,基类实现了Dispose
/// </summary>
public class DerivedClass : DisposePattern
{
// 是否释放
private bool _isDisposed = false;
// 锁对象
private static readonly object _clockObj = new object();
protected override void Dispose(bool disposing)
{
//不需要多次释放
if (_isDisposed) return;
lock (_clockObj)
{
try
{
//清理自己的资源,实现模式与DisposePattern相同
if (disposing)
{
//释放托管资源
}
//释放非托管资源
}
finally
{
//基类释放自己的资源
base.Dispose(disposing);
_isDisposed = true;
}
}
}
}
当然,如果DerivedClass本身没有什么资源需要清理,那么就不需要重写Dispose方法。