すでに多くの良い議論がここにあり、私はパーティーに少し遅れましたが、私はいくつかのポイントを自分で付け加えたかったです。
- ガベージコレクションは、Disposeメソッドを直接実行することはありません。
- GC は、必要に応じてファイナライザを実行します。
- ファイナライザを持つオブジェクトに使用される一般的なパターンの1つは、Dispose(bool disposing)として定義されているメソッドを呼び出し、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を呼び出しても何も実行されません)。
- 使い捨てオブジェクトの継承階層を適切に管理することは非常に複雑になる可能性があります。特に、さまざまなレイヤーが新しい使い捨ておよび管理対象外のリソースを導入する場合はなおさらです。上記のパターンでは、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();
}
}