C#でのIDisposableとデストラクタの使用の違いは何ですか?


101

いつデストラクタではなくクラスにIDisposeを実装しますか?この記事を読みましたが、まだ要点がありません。

私の想定では、IDisposeをオブジェクトに実装すると、ガベージコレクターがIDisposeを実行するのを待つのではなく、明示的に「破棄」できるということです。これは正しいです?

つまり、常にオブジェクトに対してDisposeを明示的に呼び出す必要がありますか?これのいくつかの一般的な例は何ですか?


5
実際、すべてのDisposableオブジェクトでDisposeを呼び出す必要があります。これは、using構成を使用して簡単に行うことができます。
Luc Touraille 2008

ああ、それは理にかなっています。なぜ 'using'ステートメントがファイルストリームに使用されたのか、いつも疑問に思っていました。私はそれがオブジェクトのスコープと関係があることを知っていましたが、IDisposableインターフェイスとの関連でそれを入れませんでした。
ジョーダンパーマー

5
覚えておくべき重要な点の1つは、クラスのマネージメンバーは有効な参照ではなくなる可能性があるため、ファイナライザーがクラスのマネージメンバーにアクセスしてはならないということです。
ダンブライアント

回答:


126

ファイナライザ(デストラクタとも呼ばれます)はガベージコレクション(GC)の一部です。GCは主にメモリプレッシャーの結果として発生するため(つまり、より多くのスペースが必要になるため)、いつ発生するか(発生しても)は不確定です。ファイナライザーは通常、管理されていないリソースのクリーンアップにのみ使用されます。管理されたリソースには独自の収集/破棄があります。

したがって、オブジェクトIDisposable確定的にクリーンアップするために使用されます。オブジェクトのメモリ(まだGCに属している)は収集しませんが、ファイルやデータベース接続などを閉じるために使用されます。

これに関するこれまでのトピックはたくさんあります:

最後に、IDisposableオブジェクトがファイナライザを持つことも珍しくないことに注意してください。この場合、Dispose()通常はを呼び出しますGC.SuppressFinalize(this)。これは、GCがファイナライザを実行しないことを意味します。メモリを捨てるだけです(はるかに安価)。Dispose()オブジェクトを忘れた場合でもファイナライザーは実行されます。


ありがとう!それは完全に理にかなっています。素晴らしい対応に感謝します。
ジョーダンパーマー、

27
もう1つ言いたいことがあります。本当に必要な場合を除き、クラスにファイナライザーを追加しないでください。ファイナライザ(デストラクタ)を追加する場合、GCはそれを呼び出す必要があり(空のファイナライザでも)、それを呼び出すには、オブジェクトは常にgen 1ガベージコレクションを通過します。これにより、GCが妨げられ、速度が低下します。それは、マークが上記のコードでSuppressFinalizeを呼び出すように言っているのです
Kevin Jones

1
したがって、ファイナライズはアンマネージリソースを解放することです。しかし、Disposeを使用して、マネージリソースとアンマネージリソースを解放できますか?
Dark_Knight 2016

2
@ダークはい; 管理チェーンの下の6つのレベルは、迅速なクリーンアップを必要とする管理されていないものになる可能性があるためです
Marc Gravell

1
ファイナライザのある@KevinJonesオブジェクトは、1ではなくgen 0で生き残ることが保証されていますよね?.NET Performanceという本でそれを読みました。
David Klempfner

25

このFinalize()メソッドの役割は、.NETオブジェクトがガベージコレクション時にアンマネージリソースを確実にクリーンアップできるようにすることです。ただし、データベース接続やファイルハンドラーなどのオブジェクトは、ガベージコレクションに依存するのではなく、できるだけ早く解放する必要があります。そのためには、IDisposableインターフェースを実装し、Dispose()メソッドでリソースを解放する必要があります。


9

MSDNには非常に良い説明があります

このインターフェイスの主な用途は、アンマネージリソース解放することです。ガベージコレクターは、そのオブジェクトが使用されなくなったときに、管理オブジェクトに割り当てられたメモリを自動的に解放します。ただし、ガベージコレクションがいつ発生するかを予測することはできません。さらに、ガベージコレクター 、ウィンドウハンドルや開いているファイルやストリームなどのアンマネージリソースを認識していません

このインターフェイスのDisposeメソッドを使用 して、ガベージコレクターと共にアンマネージリソースを明示的に解放します。オブジェクトコンシューマは、オブジェクトが不要になったときにこのメソッドを呼び出すことができます。


1
その説明の主な弱点の1つは、MSが管理されていないリソースの例を提供することですが、私が見たものから、実際に用語を定義することはありません。マネージオブジェクトは一般にマネージコード内でのみ使用できるため、アンマネージコードで使用されるものはアンマネージリソースであると考える人もいるかもしれませんが、実際にはそうではありません。多くのアンマネージコードはリソースを使用せず、イベントなどのある種のアンマネージリソースはマネージコードユニバースにのみ存在します。
スーパーキャット2015年

1
短命のオブジェクトが長命のオブジェクトからのイベントにサブスクライブする場合(たとえば、短命のオブジェクトの存続期間内に発生する変更の通知を要求する場合)、そのようなイベントは管理対象外のリソースと見なされます。イベントをサブスクライブ解除すると、短命のオブジェクトの存続期間が長命のオブジェクトの存続期間に延長されます。数千または数百万の短期間のオブジェクトがイベントにサブスクライブしたが、サブスクライブ解除せずに破棄された場合、メモリまたはCPUリークが発生する可能性があります(各サブスクリプションの処理に必要な時間が増加するため)。
スーパーキャット2015年

1
マネージコード内のアンマネージリソースを含む別のシナリオは、プールからのオブジェクトの割り当てです。特に、コードが.NET Micro Framework(ガベージコレクターはデスクトップマシンのガベージコレクターよりもはるかに効率が悪い)で実行する必要がある場合、コードが、たとえばそれぞれが「使用済み」とマークされている可能性がある構造の配列を持つと役立つ場合があります。または「無料」。割り当て要求は、現在「空き」とマークされている構造を見つけ、「使用済み」とマークし、それにインデックスを返す必要があります。リリースリクエストは、構造を「無料」としてマークする必要があります。割り当て要求が23を返す場合、たとえば、
supercat

1
...コードが配列の所有者にアイテム#23が不要になったことを通知しない場合、その配列スロットは他のコードで使用できません。このような配列スロットからの手動割り当ては、GCがかなり効率的であるため、デスクトップコードではそれほど頻繁に使用されませんが、Micro Frameworkで実行されるコードでは、大きな違いが生じる可能性があります。
スーパーキャット2015年

8

C#デストラクタにあるべき唯一のものは次の行です。

Dispose(False);

それでおしまい。そのメソッドに他のものはありません。


3
これはMicrosoftが.NETドキュメントで提案した設計パターンですが、オブジェクトがIDisposableでない場合は使用しないでください。 msdn.microsoft.com/en-us/library/fs2xkftw%28v=vs.110%29.aspx
Zbyl 2013年

1
Disposeメソッドを持たないファイナライザーを備えたクラスを提供する理由は何も考えられません。
ジョナサンアレン

4

常に電話をかけるべきかどうかについてのあなたの質問Disposeは通常白熱した議論です。.NETコミュニティで尊敬される個人からの興味深い見解については、このブログを参照してください。

個人Dispose的には、呼び出しは必須ではないというジェフリー・リヒターの立場は信じられないほど弱いと思います。彼は自分の意見を正当化するために2つの例を挙げています。

最初の例ではDispose、主流のシナリオではWindowsフォームコントロールを呼び出すのは面倒で不要です。ただし、Dispose主流のシナリオでは、実際にはコントロールコンテナーによって自動的に呼び出されることについては触れていません。

2番目の例ではIAsyncResult.WaitHandle、プロパティが待機ハンドルを遅延初期化することを認識せずに、インスタンスfrom を積極的に破棄する必要があると開発者が誤って想定し、不要なパフォーマンスペナルティが発生する可能性があると述べています。しかし、この例の問題は、IAsyncResultそれ自体がIDisposableオブジェクトを処理するためのMicrosoft独自の公開ガイドラインに準拠していないことです。つまり、クラスがIDisposable型への参照を保持している場合、クラス自体が実装する必要がありますIDisposableIAsyncResultそのルールに従えば、独自のDispose方法で、構成メンバーのどれを処分する必要があるかを決定できます。

したがって、誰かがより説得力のある議論をしない限り、私は「常にDisposeを呼び出す」キャンプにとどまり、主に不適切な設計の選択から生じるいくつかの周辺のケースがあることを理解します。


3

とてもシンプルです。回答済みであることは承知していますが、もう一度やり直しますが、できる限りシンプルにするよう努めます。

デストラクタは通常使用しないでください。.netは実行したいだけです。ガベージコレクトインサイクルの後でのみ実行されます。アプリケーションのライフサイクル中に実際に実行されることはありません。このため、「実行する必要がある」デストラクタにコードを配置しないでください。また、実行時にクラス内の既存のオブジェクトに依存することもできません(デストラクタが実行される順序が保証されていないため、既にクリーンアップされている可能性があります)。

IDisposibleは、クリーンアップが必要なリソース(つまり、ファイルおよびグラフィックスハンドル)を作成するオブジェクトがある場合は常に使用する必要があります。実際、多くの人は、上記の理由により、デストラクタに入れるものはすべてIDisposableにする必要があると主張しています。

ほとんどのクラスは、ファイナライザが実行されるときにdisposeを呼び出しますが、これは単に安全策として存在するものであり、決して信頼してはなりません。使い終わったら、IDisposableを実装するものはすべて明示的に破棄する必要があります。IDisposableを実装する場合は、ファイナライザでdisposeを呼び出す必要があります。例については、http://msdn.microsoft.com/en-us/library/system.idisposable.aspxを参照してください


いいえ、ガベージコレクターはDispose()を呼び出しません。それは唯一のファイナライザを呼び出します。
Marc Gravell

修正しました。クラスはファイナライザでdisposeを呼び出すことになっていますが、そうする必要はありません。
DaEagle 2008

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.