web-dev-qa-db-ja.com

「破棄」は、管理されていないリソースを含むタイプにのみ使用する必要がありますか?

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

できるだけ早くクリーンアップする必要があるタイプにIDisposableを実装することには価値があると思います、クリーンアップするアンマネージリソースがない場合でも

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

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

推奨されるディスポーザブルパターン ここでDisposeが呼び出された場合に管理対象リソースと管理対象外のリソースを解放するしかしファイナライザー/デストラクタ(そして、その方法について少し前にブログに書いています IDisposableタイプの不適切な使用について消費者に警告します

したがって、私の質問は、管理されていないリソースを含まないタイプがある場合、IDisposableを実装する価値があるかどうかです。

65
Steve Dunn

IDisposableにはさまざまな有効な用途があります。簡単な例は、開いているファイルを保持することです。このファイルは、必要がなくなったらすぐに閉じる必要があります。もちろん、メソッドCloseを提供することもできますが、それをDisposeに置き、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 [〜#〜]を超えるプログラムのコピーを同時に実行することは許可されていないことを想像してください)など。解放されるオブジェクトはアンマネージリソースではありませんが、プログラムロジックの純粋に内部構造である何かを実行/使用するためのrightです。


小さな追加:ここにIDisposableを使用する[ab]の素敵な例の小さなリストがあります: http://www.introtorx.com/Content/v1.0.10621.0/03_LifetimeManagement.html#IDisposable

35
Vlad

IDisposableを次の観点から考えることが最も役立つと思います 責任。オブジェクトは、不要になってから宇宙の終焉までの間に(できればできるだけ早く)実行する必要があることを知っていて、それが唯一のオブジェクトである場合は、IDisposableを実装する必要があります。情報とそれを実行するための推進力の両方。たとえば、ファイルを開くオブジェクトには、ファイルが閉じられることを確認する責任があります。ファイルを閉じずにオブジェクトが単に消えた場合、ファイルは適切な時間枠で閉じられない可能性があります。

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

何らかの理由で、最初にDisposeが呼び出されずにオブジェクトが破棄された場合、クリーンアップにファイナライザーを使用できる場合があることに注意してください。時々これはうまくいきます。いくつかのそれは非常にひどく動作します。たとえば、Microsoft.VisualBasic.Collectionファイナライザーを使用して、「変更された」イベントから列挙子をデタッチします。Disposeまたはガベージコレクションを介さずにそのようなオブジェクトを数千回列挙しようとすると、オブジェクトの速度が非常に遅くなります。 Disposeを正しく使用した場合のパフォーマンスよりも優れています。

17
supercat

したがって、私の質問は、管理されていないリソースを含まないタイプがある場合、IDisposableを実装する価値があるかどうかです。

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

実はそれは判断の呼びかけです。その時点でガベージコレクターを用意する必要があるので、過度に実装する必要はありません。すべてのオブジェクトを手動で破棄しないのはなぜですか。管理されていないリソースを破棄する必要がある可能性がある場合、それは悪い考えではないかもしれません。オブジェクトを使用しているのがチームのメンバーだけである場合は、後でいつでもフォローアップして、「今すぐ管理されていないリソースを使用する必要があります。コードを確認して確認する必要があります。片付けました。」他の組織が使用するためにこれを公開している場合は異なります。そのオブジェクトを実装した可能性のあるすべての人に、「これが破棄されたことを確認する必要があります」と伝える簡単な方法はありません。サードパーティのアセンブリをアップグレードして、コードを変更し、アプリケーションでメモリの問題が発生したことを確認するよりも、人々を怒らせるものはほとんどありません。

私の同僚は、内部では、ADO.NET接続は管理対象リソースであると回答しました。彼の返事に対する私の返事は、すべてが最終的には管理されていないリソースであるというものでした。

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

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

9
kemiller2002

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

IDisposableを実装する場合は3つあります。

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

私は少し偏見があるかもしれませんが、あなたは本当に読むべきです(そしてあなたの同僚を見せるべきです) IDisposableのStackOverflowWiki

6
Adam Robinson

Disposeは、有効期間が制限されていますのリソースに使用する必要があります。ファイナライザーは、管理されていないリソースに使用する必要があります。管理されていないリソースの有効期間は制限されている必要がありますが、有効期間が制限されている管理対象リソース(ロックなど)はたくさんあります。

5
Gabe

いいえ、管理されていないリソースの場合はnotonlyです。

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

5
Tigran

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

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

5
Jacek Gorgoń

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

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

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

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

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

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

管理されていないリソースがない使い捨てクラスは次のとおりです。

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

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

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

destructor は、継承チェーン内のすべてのFinalizeObject.Finalize()まで暗黙的に呼び出すことに注意してください。

そして、これがコンソールアプリのMainメソッドです。

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

    Console.ReadKey();
}
_

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

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

[〜#〜]編集[〜#〜]

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

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

// back to your user
_

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

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

3
Maciej

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

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

3
Steve Townsend

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

3

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

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

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

1
M Afifi

短い答え:絶対にありません。タイプに管理対象または管理対象外のメンバーがある場合は、IDisposableを実装する必要があります。

詳細:この質問に回答し、メモリ管理の内部と、StackOverflowの質問に関するGCについてさらに詳しく説明しました。ここにいくつかあります:

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

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

1
Dave Black

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

1
justin.m.chase

不要リソースはまったくありません(管理対象または非管理対象)。多くの場合、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();
  }
0
Dmitry Bychenko

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

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

0
Igor Pashchuk