ここにはすでに多くの良い議論があり、パーティーに少し遅れてしまいましたが、自分でいくつかのポイントを追加したいと思いました.
- ガベージ コレクターが Dispose メソッドを直接実行することはありません。
- GCは、必要に応じてファイナライザーを実行します。
- ファイナライザーを持つオブジェクトに使用される一般的なパターンの 1 つは、明示的な Dispose 呼び出しではなく、ファイナライズによって呼び出しが行われたことを示すために、false を渡す Dispose(bool disposing) として慣例により定義されているメソッドを呼び出すことです。
- これは、オブジェクトをファイナライズするときに、他の管理対象オブジェクトに関する仮定を行うのは安全ではないためです (既にファイナライズされている可能性があります)。
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();
}
}