这里已经有很多很好的讨论了,我参加聚会有点晚了,但是我想自己补充一点。
- 垃圾收集器将永远不会直接为您执行Dispose方法。
- GC 会在需要时执行终结器。
- 用于具有终结器的对象的一种常见模式是让其调用一种方法,该方法按照惯例定义为Dispose(bool dispose)传递false来表示该调用是由于终结器而不是显式的Dispose调用。
- 这是因为在完成对象时对其他托管对象进行任何假设都是不安全的(它们可能已经完成)。
class SomeObject : IDisposable {
IntPtr _SomeNativeHandle;
FileStream _SomeFileStream;
// Something useful here
~ SomeObject() {
Dispose(false);
}
public void Dispose() {
Dispose(true);
}
protected virtual void Dispose(bool disposing) {
if(disposing) {
GC.SuppressFinalize(this);
//Because the object was explicitly disposed, there will be no need to
//run the finalizer. Suppressing it reduces pressure on the GC
//The managed reference to an IDisposable is disposed only if the
_SomeFileStream.Dispose();
}
//Regardless, clean up the native handle ourselves. Because it is simple a member
// of the current instance, the GC can't have done anything to it,
// and this is the onlyplace to safely clean up
if(IntPtr.Zero != _SomeNativeHandle) {
NativeMethods.CloseHandle(_SomeNativeHandle);
_SomeNativeHandle = IntPtr.Zero;
}
}
}
那是简单的版本,但是有很多细微差别可以使您陷入这种模式。
- IDisposable.Dispose的合同表明多次调用必须安全(在已被处置的对象上调用Dispose不应执行任何操作)
- 正确管理一次性对象的继承层次结构可能会变得非常复杂,尤其是如果不同的层引入了新的Disposable和不受管理的资源时。在上面的模式中,Dispose(bool)是虚拟的,允许对其进行重写以便可以对其进行管理,但是我发现它容易出错。
我认为,最好完全避免使用直接包含一次性引用和可能需要最终确定的本机资源的任何类型。SafeHandles通过将本机资源封装到内部提供自己的终结处理的一次性资源中,提供了一种非常干净的方式(以及许多其他好处,例如在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;
// Something useful here
public virtual void Dispose() {
_SomeSafeHandle.Dispose();
_SomeFileStream.Dispose();
}
}