67

Dispose最近、同僚と の値と を実装する型について話し合っていましたIDisposable

クリーンアップするアンマネージ リソースがなくてもIDisposable、できるだけ早くクリーンアップする必要がある型に対して実装する価値があると思います。

私の同僚は別の考え方をしています。IDisposableあなたのタイプは最終的にガベージコレクションされるため、管理されていないリソースがない場合は実装する必要はありません。

私の主張は、できるだけ早く閉じたい ADO.NET 接続があれば、IDisposableandを実装するのusing new MyThingWithAConnection()が理にかなっているというものでした。私の同僚は、裏では ADO.NET 接続は管理されていないリソースであると答えました。彼の返答に対する私の返答は、最終的にはすべてが管理されていないリソースであるというものでした。

が呼び出された場合はマネージド リソースとアンマネージド リソースを解放するが、ファイナライザー/デストラクタ経由で呼び出された場合はアンマネージド リソースのみを解放する推奨される破棄可能なパターンを認識しています(また、IDisposable 型の不適切な使用を消費者に警告する方法について少し前にブログを書きました) 。Dispose

それで、私の質問は、管理されていないリソースを含まないタイプを持っている場合、それを実装する価値はありIDisposableますか?

4

15 に答える 15

36

にはさまざまな有効な用途がありますIDisposable。簡単な例は、開いているファイルを保持していて、必要がなくなったらすぐに閉じる必要がある場合です。もちろん、 method を提供することもできますが、CloseそれをDispose使用して pattern like を使用using (var f = new MyFile(path)) { /*process it*/ }すると、より例外に対して安全になります。

より一般的な例は、他のIDisposableリソースを保持することです。これは通常、それらを破棄するために独自のものを提供する必要があることを意味しますDispose

一般に、何かを決定論的に破壊したい場合は、すぐに を実装する必要がありますIDisposable

私の意見とあなたの意見の違いは、IDisposable一部のリソースが決定論的な破棄/解放を必要とする場合にすぐに実装することであり、できるだけ早く実装する必要はありませ。この場合、ガベージ コレクションに依存することはできません (同僚の主張に反して)。ガベージ コレクションは予測できない瞬間に発生し、実際にはまったく発生しない可能性があるためです。

リソースが隠れて管理されていないという事実は、実際には何の意味もありません。開発者は、「隠れてどのように機能するか」ではなく、「このオブジェクトをいつ、どのように破棄するのが正しいか」という観点から考える必要があります。とにかく、基本的な実装は時間とともに変化する可能性があります。

実際、C# と C++ の主な違いの 1 つは、既定の決定論的破棄がないことです。はIDisposableギャップを埋めます: 決定論的破壊を命令することができます (ただし、クライアントがそれを呼び出していることを確認することはできません。C++ と同じように、クライアントがdeleteオブジェクトを呼び出していることを確認することはできません)。


ちょっとした追加: リソースを決定論的に解放することと、できるだけ早く解放することの実際の違いは何ですか? 実際には、これらは (完全に直交するわけではありませんが) 異なる概念です。

リソースが決定論的に解放される場合、これは、クライアント コードが「今、このリソースを解放したい」と言う可能性を持つ必要があることを意味します。これは実際には、リソースが解放される可能性がある最も早い時期ではない可能性があります。リソースを保持しているオブジェクトは、リソースから必要なものをすべて取得している可能性があるため、リソースをすでに解放している可能性があります。一方、オブジェクトは、オブジェクトがDispose実行された後でも (通常は管理されていない) リソースを保持することを選択し、ファイナライザーでのみクリーンアップします (リソースを長時間保持しても問題がない場合)。

そのため、厳密に言えば、リソースをできるだけ早くDispose解放する必要はありません。オブジェクトは、リソースが不要になったことを認識するとすぐにリソースを解放できます。ただし、オブジェクト自体はもう必要ないDisposeという有用なヒントとして機能するため、必要に応じてその時点でリソースが解放される可能性があります。


もう 1 つ追加する必要があります。確定的な割り当て解除が必要なのは、管理されていないリソースだけではありません。これは、この質問に対する回答の意見の相違の重要なポイントの 1 つと思われます。決定論的に解放する必要があるかもしれない、純粋に想像力に富んだ構成を持つことができます。

例: 共有構造にアクセスする権利 ( RW-lockを考えてください)、巨大なメモリ チャンク (プログラムのメモリの一部を手動で管理していると想像してください)、他のプログラムを使用するためのライセンス (許可されていないことを想像してください)いくつかのプログラムのX個以上のコピーを同時に実行する) などです


ちょっとした追加: ここに [ab]using のきちんとした例の小さなリストがありますIDisposable: http://www.introtorx.com/Content/v1.0.10621.0/03_LifetimeManagement.html#IDisposable .

于 2012-04-25T13:14:55.927 に答える
17

責任IDisposableの観点から考えるのが最も役立つと思います。オブジェクトは、不要になった時点から宇宙の終わりまで (できればできるだけ早く) 実行する必要があることを認識している場合、およびその情報と原動力の両方を備えた唯一のオブジェクトである場合に実装する必要があります。やれ。たとえば、ファイルを開くオブジェクトには、ファイルが閉じられることを確認する責任があります。ファイルを閉じずにオブジェクトが単に消えた場合、ファイルは妥当な時間内に閉じられない可能性があります。IDisposable

100% 管理されたオブジェクトとのみ対話するオブジェクトでさえ、クリーンアップが必要な (および を使用する必要があるIDisposable) ことを実行できることに注意することが重要です。たとえばIEnumerator、コレクションの「変更された」イベントにアタッチされている は、不要になったときにそれ自体をデタッチする必要があります。それ以外の場合、列挙子が複雑なトリックを使用しない限り、コレクションがスコープ内にある限り、列挙子がガベージ コレクションされることはありません。コレクションが 100 万回列挙されると、100 万個の列挙子がそのイベント ハンドラーにアタッチされます。

Dispose何らかの理由でオブジェクトが最初に呼び出されずに放棄された場合、クリーンアップにファイナライザーを使用できる場合があることに注意してください。これでうまくいく場合もあります。時々それは非常にうまく機能しません。たとえば Microsoft.VisualBasic.Collection、ファイナライザーを使用して「変更された」イベントから列挙子を切り離しても、介入Disposeやガベージ コレクションを行わずにそのようなオブジェクトを何千回も列挙しようとすると、パフォーマンスが大幅に低下します。これは、Dispose正しく使用した場合に発生します。

于 2012-04-25T13:45:42.767 に答える
9

それで、私の質問は、管理されていないリソースを含まない型を持っている場合、IDisposable を実装する価値はありますか?

誰かがオブジェクトに IDisposable インターフェイスを配置すると、作成者がそのメソッドで何かを行うつもりであるか、将来的に行う可能性があることがわかります。念のため、このインスタンスでは常に dispose を呼び出します。今は何もしなくても、将来はそうなるかもしれません。また、オブジェクトが更新され、最初にコードを書いたときに Dispose を呼び出さなかったためにメモリ リークが発生するのは残念です。

実際には、それは判断の呼びかけです。その時点で、ガベージコレクターをわざわざ用意する必要があるからです。すべてのオブジェクトを手動で破棄してみませんか。管理されていないリソースを破棄する必要がある可能性がある場合、それは悪い考えではないかもしれません。オブジェクトを使用しているのがチームのメンバーだけである場合は、後でいつでもフォローアップして、「これはアンマネージ リソースを今すぐ使用する必要がある。コードを調べて確認する必要がある」と言うことができます。私たちは片付けました。」他の組織が使用するためにこれを公開している場合、それは異なります。そのオブジェクトを実装した可能性のあるすべての人に、「これが破棄されたことを確認する必要があります」と簡単に伝える方法はありません。

私の同僚は、裏では ADO.NET 接続はマネージド リソースであると答えました。彼の返答に対する私の返答は、最終的にはすべてが管理されていないリソースであるというものでした。

彼はそうです、それは現在管理されたリソースです。彼らはそれを変えるでしょうか?誰が知っているかはわかりませんが、それを呼んでも害はありません。私は ADO.NET チームが何をしているのかを推測しようとはしません。1 行のコードが私の生産性に影響を与えることはないので、私はまだそれを呼び出します。

また、別のシナリオに遭遇します。メソッドから ADO.NET 接続を返すとします。ADO 接続がベース オブジェクトであるか、すぐに派生した型であるかはわかりません。その IDisposable の実装が突然必要になったかどうかはわかりません。実稼働サーバーが 4 時間ごとにクラッシュしている場合、実稼働サーバーでのメモリ リークを追跡するのは面倒なので、私は常にそれを呼び出します。

于 2012-04-25T13:18:32.627 に答える
6

これにはすでに良い答えがありますが、私は何かを明確にしたかっただけです。

を実装するには、次の 3 つのケースがありますIDisposable

  1. アンマネージ リソースを直接使用しています。これには、通常IntPrt、別の P/Invoke 呼び出しによって解放される必要がある P/Invoke 呼び出しから、またはその他の形式のハンドルを取得することが含まれます。
  2. あなたは他のIDisposableオブジェクトを使用しており、それらの処分に責任を持つ必要があります
  3. usingブロックの利便性など、他に必要なものや使用するものがあります。

少し偏見があるかもしれませんが、上の StackOverflow Wiki をIDisposable実際に読んで (そして同僚に見せて) ください。

于 2012-04-25T18:21:49.100 に答える
5

Dispose should be used for any resource with a limited lifetime. A finalizer should be used for any unmanaged resource. Any unmanaged resource should have a limited lifetime, but there are plenty of managed resources (like locks) that also have limited lifetimes.

于 2012-04-25T13:20:21.797 に答える
5

いいえ、管理されていない リソースだけではありません。

フレームワークによって呼び出される基本的なクリーンアップ組み込みメカニズムのように提案されており、必要なリソースをクリーンアップできる可能性がありますが、自然に管理されていないリソース管理が最適です。

于 2012-04-25T13:16:12.993 に答える
5

アンマネージ リソースには標準の CLR オブジェクトが含まれる可能性があることに注意してください。たとえば、いくつかの静的フィールドに保持され、すべてアンマネージ インポートなしでセーフ モードで実行されます。

特定のクラスの実装IDiposableが実際に何かをクリーンアップする必要があるかどうかを判断する簡単な方法はありません。私の経験則は、Disposeサードパーティのライブラリのように、あまりよく知らないオブジェクトを常に呼び出すことです。

于 2012-04-25T13:18:21.300 に答える
3

あなたが正しいです。管理されたデータベース接続、ファイル、レジストリ キー、ソケットなどはすべて、管理されていないオブジェクトを保持します。それが彼らが実装する理由IDisposableです。型が使い捨てオブジェクトを所有している場合は、IDisposableそれらをDisposeメソッドに実装して破棄する必要があります。そうしないと、ガベージ コレクションが行われるまで存続し、ファイルがロックされたり、その他の予期しない動作が発生したりする可能性があります。

于 2012-04-25T13:17:51.113 に答える
3

を集約する場合IDisposableは、それらのメンバーがタイムリーにクリーンアップされるようにインターフェースを実装する必要があります。myConn.Dispose()あなたが引用したADO.Net接続の例では、他にどのように呼び出されるのでしょうか?

ただし、このコンテキストですべてが管理されていないリソースであると言うのは正しくないと思います。あなたの同僚にも同意しません。

于 2012-04-25T13:17:03.187 に答える
3

最終的にはすべてが管理されていないリソースになります。

違います。フレームワークによってのみ管理 (割り当ておよび解放) される CLR オブジェクトによって使用されるメモリを除くすべて。

管理されていないリソースを保持していないオブジェクトを(直接または依存オブジェクトを介して間接的に)実装IDisposableして呼び出すことは無意味です。オブジェクトの CLR メモリを自分で直接解放することはできないため、そのオブジェクトの解放を決定論的にすることはできませ。メソッドレベルで直接使用される場合、値型はスタック操作によって割り当て/解放されるため、オブジェクトは参照型です。Dispose GC

今では、誰もが自分の答えが正しいと主張しています。私の証明をさせてください。ドキュメントによると:

Object.Finalize メソッドを使用すると、オブジェクトはガベージ コレクションによって回収される前に、リソースを解放し、その他のクリーンアップ操作を実行できます。

Object.Finalize()つまり、オブジェクトの CLR メモリは呼び出された直後に解放されます。[注: 必要に応じて、この呼び出しを明示的にスキップすることができます]

アンマネージ リソースのない使い捨てクラスを次に示します。

internal class Class1 : IDisposable
{
    public Class1()
    {
        Console.WriteLine("Construct");
    }

    public void Dispose()
    {
        Console.WriteLine("Dispose");
    }

    ~Class1()
    {
        Console.WriteLine("Destruct");
    }
}

デストラクタFinalizeは、継承チェーンのすべてを暗黙的に呼び出すことに注意してください。Object.Finalize()

コンソール アプリのMainメソッドは次のとおりです。

static void Main(string[] args)
{
    for (int i = 0; i < 10; i++)
    {
        Class1 obj = new Class1();
        obj.Dispose();
    }

    Console.ReadKey();
}

呼び出しが決定論的な方法で管理Dispose対象オブジェクトを解放する方法である場合、すべての「破棄」の直後に「破棄」が続きますよね? 何が起こるか自分の目で確かめてください。コマンド ライン ウィンドウからこのアプリを実行するのが最も興味深いです。

注:GC現在のアプリ ドメインでファイナライズが保留されているすべてのオブジェクトを強制的に収集する方法がありますが、特定の 1 つのオブジェクトについてはそうではありません。Disposeただし、オブジェクトをファイナライズ キューに入れるために呼び出す必要はありません。アプリケーション全体のパフォーマンスが低下する可能性があるため、収集を強制することは強くお勧めしません。

編集

1 つの例外があります - 状態管理です。Disposeオブジェクトがたまたま外部の状態を管理している場合、状態の変更を処理できます。状態が管理されていないオブジェクトでなくても、特別な扱いIDisposableがあるため、状態のように使用すると非常に便利です。例としては、セキュリティ コンテキストまたは偽装コンテキストがあります。

using (WindowsImpersonationContext context = SomeUserIdentity.Impersonate()))
{
    // do something as SomeUser
}

// back to your user

WindowsImpersonationContext内部でシステム ハンドルを使用するため、これは最良の例ではありませんが、画像は得られます。

要するに、実装するときIDisposableは、メソッドで何か意味のあることを行う必要がある (または計画する) 必要があるということですDispose。そうでなければ、ただの時間の無駄です。IDisposableGC によるオブジェクトの管理方法は変わりません。

于 2012-05-01T20:55:07.967 に答える
1

私のプロジェクトの1つでは、管理されたスレッドを内部に持つクラスがあり、それらをスレッドA、スレッドBと呼び、IDisposableオブジェクトをCと呼びます。

終了時にCを破棄するために使用されます。Bは、Cを使用して例外を保存するために使用されていました。

私のクラスは、物事が正しい順序で廃棄されるように、IDisposableと記述子を実装する必要がありました。はい、GCは私のアイテムをクリーンアップできましたが、クラスのクリーンアップを管理しない限り、競合状態が発生したという経験がありました。

于 2012-05-18T15:31:12.643 に答える
1

短い答え: 絶対にありません。型にマネージドまたはアンマネージドのメンバーがある場合は、IDisposable を実装する必要があります。

詳細: この質問に回答し、メモリ管理の内部と GC の質問に関する詳細を StackOverflow で提供しました。以下にいくつかを示します。

IDisposable の実装に関するベスト プラクティスについては、私のブログ投稿を参照してください。

IDisposable パターンを適切に実装するにはどうすればよいですか?

于 2012-08-31T14:16:28.750 に答える
1

まったく必要ないリソース(マネージドまたはアンマネージド)。多くの場合、面倒な を排除IDisposableする便利な方法try {..} finally {..}です。比較してください:

  Cursor savedCursor = Cursor.Current;

  try {
    Cursor.Current = Cursors.WaitCursor;

    SomeLongOperation();
  }
  finally {
    Cursor.Current = savedCursor;
  }

  using (new WaitCursor()) {
    SomeLongOperation();
  }

どこWaitCursorIDisposable適していますかusing

  public sealed class WaitCursor: IDisposable {
    private Cursor m_Saved;

    public Boolean Disposed {
      get;
      private set;
    }

    public WaitCursor() {
      Cursor m_Saved = Cursor.Current;
      Cursor.Current = Cursors.WaitCursor;
    }

    public void Dispose() {
      if (!Disposed) {
        Disposed = true;
        Cursor.Current = m_Saved;
      }
    }
  }

このようなクラスを簡単に組み合わせることができます。

  using (new WaitCursor()) {
    using (new RegisterServerLongOperation("My Long DB Operation")) {
      SomeLongRdbmsOperation();  
    }

    SomeLongOperation();
  }
于 2015-10-26T16:08:30.430 に答える
1

タイプがアンマネージ リソースを参照する場合、または IDisposable を実装するオブジェクトへの参照を保持する場合、タイプは IDisposable を実装する必要があります。

于 2012-04-25T14:26:44.033 に答える
1

オブジェクトが管理されていないオブジェクトまたは管理された破棄可能IDisposableなオブジェクトを所有している場合に実装します

オブジェクトがアンマネージ リソースを使用する場合は、 を実装する必要がありますIDisposable。使い捨てオブジェクトを所有するオブジェクトはIDisposable、基になるアンマネージ リソースが確実に解放されるように実装する必要があります。したがって、ルール/慣例に従えば、管理された使い捨てオブジェクトを破棄しないことは、管理されていないリソースを解放しないことと同じであると結論付けるのが論理的です。

于 2015-08-01T03:10:26.037 に答える