这里已经有很多好的讨论了,我有点晚来参加,但我想自己添加一些观点。
- 垃圾回收器永远不会直接执行Dispose方法。
- GC会在感觉需要时执行终结器。
- 一种常见的模式是,对于具有终结器的对象,将其调用一个按照约定定义为Dispose(bool disposing)的方法,并传递false以指示该调用是由于终结而不是显式Dispose调用。
- 这是因为在终止对象时不能对其他托管对象进行任何假设(它们可能已经被终止)。
class SomeObject : IDisposable {
IntPtr _SomeNativeHandle;
FileStream _SomeFileStream;
~ SomeObject() {
Dispose(false);
}
public void Dispose() {
Dispose(true);
}
protected virtual void Dispose(bool disposing) {
if(disposing) {
GC.SuppressFinalize(this);
_SomeFileStream.Dispose();
}
if(IntPtr.Zero != _SomeNativeHandle) {
NativeMethods.CloseHandle(_SomeNativeHandle);
_SomeNativeHandle = IntPtr.Zero;
}
}
}
这是简单的版本,但是这种模式有很多微妙之处可能会让你陷入困境。
IDisposable.Dispose的合同表明,可以安全地多次调用它(在已经处理过的对象上调用Dispose应该什么都不做)。
如果不同层引入新的可处理和未管理的资源,正确地管理可处理对象的继承层次结构会变得非常复杂。在上面的模式中,Dispose(bool)是虚拟的,以允许覆盖它,以便可以进行管理,但我发现这很容易出错。
在我看来,最好完全避免直接包含既包含一次性引用又包含可能需要最终处理的本机资源的任何类型。 SafeHandle通过将本地资源封装到可处理资源中,并在内部提供自己的终结(以及许多其他好处,例如在P / Invoke期间删除窗口,其中由于异步异常可能会丢失本机句柄),提供了非常清晰的方法。
简单地定义一个SafeHandle即可实现此目的。
private class SomeSafeHandle
: SafeHandleZeroOrMinusOneIsInvalid {
public SomeSafeHandle()
: base(true)
{ }
protected override bool ReleaseHandle()
{ return NativeMethods.CloseHandle(handle); }
}
允许您简化包含类型为:
class SomeObject : IDisposable {
SomeSafeHandle _SomeSafeHandle;
FileStream _SomeFileStream;
public virtual void Dispose() {
_SomeSafeHandle.Dispose();
_SomeFileStream.Dispose();
}
}