IDisposableと、デストラクタを除く完全な、、およびパターンを実装する抽象基本クラスがbool disposed = false
ありDispose()
ますDispose(bool)
。基本クラスは、派生クラスの多くがアンマネージリソースを解放する必要があるため、IDisposableを実装します。ただし、デストラクタを含むクラスは高価であるため、デストラクタを含めると、管理されていないリソースを持たない派生クラスが不必要に高価になると聞きました。私はこの問題について混乱しています。デストラクタを含める必要がありますか、含めない必要がありますか。その理由は何ですか。ありがとう。
4 に答える
まったく新しい種類のアンマネージリソースを実装する場合にのみ、デストラクタ/ファイナライザを含める必要があります。したがって、既存のデータベース接続タイプ、ソケットタイプ、gdiリソースなどをラップまたは継承するだけの場合は、デストラクタは必要ありません。元のタイプのデストラクタが、最終的にそのリソースを解放します。ただし、まったく新しい種類のデータベース用にADO.Netプロバイダーオブジェクトのようなものを最初から実装する場合は、接続タイプのデストラクタを実装して、最終的に収集されたときに接続を解放できるようにする必要があります。
理想的には、Disposeパターンはファイナライザーにも依存して完成します。その理由は、管理されていないリソースが確実にクリーンアップされるようにするためです。ここでの秘訣は、Disposeメソッドで次の呼び出しも行う必要があることです。GC.SuppressFinalize(this)は、ガベージコレクターに、インスタンスを特別な方法で処理しないように指示します。これにより、ファイナライズのオーバーヘッドを回避できます。したがって、ユーザーがオブジェクトを毎回正しく処理する場合(usingブロックですべての使用法をラップするなど)、ファイナライザーは呼び出されないため、パフォーマンスにはまったく影響しません。
デストラクタを使ったクラスは高額だと聞きました
ファイナライザーまたはの実装があるクラスは、IDisposable
ないクラスよりも高価ではありません。ただし、実装しているクラスIDisposable
は、呼び出し元に、不要になったときに追跡してクリーンアップする必要があることを伝えています。これは呼び出し元にとって追加の作業ですが、そうしないと、少なくともクラスがガベージコレクションされるまで、リソースリークが発生します。
つまり、クラスがクリーンアップする必要のあるリソースを使用しない場合、通常はIDisposableも実装するフィールドの形式で、ファイナライザーは必要ありません。
クリーンアップの目的でオーバーライドする必要がある唯一のクラスは、独自のリソースFinalize
から直接派生してクリーンアップすることを期待するクラス、またはリソースのクリーンアップを管理することを目的とするなどのクラスです。それ以外の場合、派生クラスにクリーンアップする必要のあるアンマネージリソースがあり、基本クラスにはない場合、適切なアプローチは通常、それぞれの個別のリソースを独自のファイナライズ可能なオブジェクトにカプセル化することです。ファイナライザーを持つオブジェクトは、ファイナライズに必要のないオブジェクトへの強い参照を保持しないようにする必要があります。これは、それらが参照を保持するすべてのオブジェクト、およびそれらのオブジェクトのいずれかが参照を保持するすべてのオブジェクトなどが存続するためです。余分なガベージコレクションの生成。オブジェクトの場合Object
SafeHandle
Finalize
George
他の多くのオブジェクトへのリンクを保持しているのは、強力なバックリンクを保持していないファイナライズ可能なオブジェクトへの参照を保持しており、George
放棄されているため、ファイナライズ可能なオブジェクトは、追加のGC生成のために保持する必要がありますがGeorge
、他のオブジェクトも保持する必要があります。直接および間接参照を保持しません。対照的に、George
それ自体が実装されFinalize
ている場合、それとそれが直接または間接参照を保持するすべてのオブジェクトを保持する必要があります。
さらに、ファイナライズ可能な大きなオブジェクトの最後の使用が実際にそのリソースの1つを使用している場合、ファイナライズによって、まれではあるが追跡が難しいヒンデンバグが発生することがあります。を介してクリーンアップできるリソースを使用するコードはFinalize
、それらのリソースがまだ使用されている間、それらのリソースを含むオブジェクトがファイナライズの対象にならないようにする必要があります。これは通常、を使用して行われGC.KeepAlive()
ます。派生クラスが追加する場合Finalize
親クラスに存在するが、親クラスがファイナライザーによるクリーンアップを期待していないリソースをクリーンアップするメソッドでは、バグが発生する可能性があります。リソースを独自のクラスにカプセル化すると、この問題を回避できます(リソースの使用中に親オブジェクトがガベージコレクションされる可能性がありますが、カプセル化オブジェクトが適切に設計されているかどうかは関係ありません-カプセル化オブジェクトのFinalize
メソッドが勝ちましたメソッドがリソースの使用を終了するまで実行しないでください)。