IDisposableインターフェイスの適切な使用


1659

Microsoftのドキュメントを読むと、IDisposableインターフェイスの「主な」使用はアンマネージリソースのクリーンアップであることがわかります。

私にとって、「アンマネージド」とは、データベース接続、ソケット、ウィンドウハンドルなどのことを意味します。ただし、マネージリソースDispose()を解放するためにメソッドが実装されているコードを見ました。あなたのために。

例えば:

public class MyCollection : IDisposable
{
    private List<String> _theList = new List<String>();
    private Dictionary<String, Point> _theDict = new Dictionary<String, Point>();

    // Die, clear it up! (free unmanaged resources)
    public void Dispose()
    {
        _theList.clear();
        _theDict.clear();
        _theList = null;
        _theDict = null;
    }

私の質問は、これにより、ガベージコレクターが使用するメモリMyCollectionが、通常よりも速く解放されるのでしょうか。

編集:これまでのところ、IDisposableを使用してデータベース接続やビットマップなどのアンマネージリソースをクリーンアップする良い例がいくつか投稿されています。しかし_theList、上記のコードに100万の文字列が含まれていて、ガベージコレクターを待つのではなく、今すぐそのメモリを解放したいとします。上記のコードはそれを達成しますか?


34
IDisposableを使用する正しい「パターン」がわかるので、私は受け入れられた答えを気に入っていますが、OPが編集で述べたように、意図した質問には答えていません。IDisposableはGCを「呼び出す」のではなく、オブジェクトを破棄可能として「マーク」するだけです。しかし、GCが起動するのを待つのではなく、「今」メモリを解放する実際の方法は何ですか?この質問はもっと議論に値すると思います。
Punit Vora

40
IDisposable何もマークしません。このDisposeメソッドは、インスタンスが使用するリソースをクリーンアップするために必要なことを実行します。これはGCとは関係ありません。
John Saunders、

4
@ジョン。分かりIDisposableます。そして、そのため、私は、IDisposableが<i>メモリの解放</ i>に役立つかどうかについて、OPの意図した質問(およびフォローアップ編集)に答えは受け入れられないと答えたのです。IDisposableメモリの解放とは何の関係もないので、リソースのみです。つまり、前述のように、OPが彼の例で行っていたように、マネージ参照をnullに設定する必要はまったくありません。したがって、彼の質問に対する正しい答えは、「いいえ、それはメモリの解放を速くすることはできません。実際、メモリを解放することはまったくなく、リソースのみです」です。しかしとにかく、あなたの入力をありがとう。
Punit Vora

9
@desigeek:これが当てはまる場合は、「IDisposableがGCを「呼び出さない」、オブジェクトを破棄可能として「マークする」だけだ」と言ってはいけません
John Saunders

5
@desigeek:確定的にメモリを解放する保証された方法はありません。GC.Collect()を呼び出すこともできますが、これは要求ではなく、丁寧な要求です。ガベージコレクションを続行するには、実行中のすべてのスレッドを中断する必要があります。詳細については、.NETセーフポイントの概念を確認してください(例:msdn.microsoft.com/en-us/library/678ysw69( v=vs.110 ))。 aspx。アンマネージコードへの呼び出しがあるなどの理由でスレッドを中断できない場合、GC.Collect()は何もしない場合があります。
コンクリートガネット2015

回答:


2609

Disposeの目的、アンマネージリソースを解放することです。ある時点で実行する必要があります。そうしないと、クリーンアップされません。ガベージコレクターは、型の変数を呼び出す方法を認識ていません。呼び出す必要があるかどうかは認識していません。DeleteHandle()IntPtrDeleteHandle()

アンマネージリソースとは何ですか?Microsoft .NET Frameworkで見つかった場合は、管理されています。自分でMSDNを調べていると、管理されません。P / Invoke呼び出しを使用して.NET Frameworkで利用可能なすべての快適な世界の外に出たものはすべて管理対象外であり、これをクリーンアップする責任があります。

あなたが公開するニーズを作成したことをオブジェクトいくつかのアンマネージリソースをクリーンアップするために、外の世界を呼び出すことができるという、方法を。メソッドには任意の名前を付けることができます。

public void Cleanup()

または

public void Shutdown()

しかし、代わりにこのメソッドには標準化された名前があります:

public void Dispose()

作成されたインターフェースもありIDisposable、それはその1つのメソッドだけを持っています。

public interface IDisposable
{
   void Dispose()
}

したがって、オブジェクトにIDisposableインターフェイスを公開させ、その方法で、アンマネージリソースをクリーンアップするための単一のメソッドを作成したことを約束します。

public void Dispose()
{
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
}

これで完了です。あなたがもっとうまくできることを除いて。


オブジェクトが何らかの種類のフレームバッファーとして250MBのSystem.Drawing.Bitmap(つまり.NETマネージビットマップクラス)を割り当てている場合はどうなりますか?確かに、これはマネージ.NETオブジェクトであり、ガベージコレクターが解放します。しかし、実際に250MBのメモリをそこに置いたままにしますか?ガベージコレクターが最終的にやって来て解放するのを待っていますか?開いているデータベース接続がある場合はどうなりますか?確かに、GCがオブジェクトをファイナライズするのを待って、その接続を開いたままにしたくないでしょう。

ユーザーが呼び出した場合Dispose()(つまり、オブジェクトを使用する予定がなくなった場合)、無駄なビットマップとデータベース接続を削除してみませんか?

だから今私たちは:

  • アンマネージリソースを取り除く(必要があるため)、および
  • 管理されたリソースを取り除く(私たちは役に立ちたいので)

したがってDispose()、これらの管理対象オブジェクトを削除するようにメソッドを更新しましょう。

public void Dispose()
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);

   //Free managed resources too
   if (this.databaseConnection != null)
   {
      this.databaseConnection.Dispose();
      this.databaseConnection = null;
   }
   if (this.frameBufferImage != null)
   {
      this.frameBufferImage.Dispose();
      this.frameBufferImage = null;
   }
}

そして、あなたがもっとうまくできることを除いて、すべてが良いです!


Dispose()あなたのオブジェクトを呼び出すのを忘れた場合はどうなりますか?次に、管理されていないリソースをリークします!

注:最終的にはガベージコレクターがバックグラウンドスレッドで実行され、未使用のオブジェクトに関連付けられたメモリを解放するため、マネージリソースはリークされません。これには、オブジェクトと、使用するすべての管理対象オブジェクト(例:BitmapおよびDbConnection)が含まれます。

人が呼ぶのを忘れた場合はDispose()、我々はできるまだ彼らのベーコンを救います!我々はまだそれを呼び出す方法持っているため、それらを:ガベージコレクタが最終的に(つまり、ファイナライズ)私たちのオブジェクトを解放するに周りに取得するとき。

注:ガベージコレクターは、最終的にすべての管理対象オブジェクトを解放します。実行するとFinalize 、オブジェクトのメソッドが呼び出されます。GCは知っている、または約ケア、しないあなた のDisposeメソッド。これは、管理されていないものを削除するときに呼び出すメソッドに選択した名前にすぎません。

ガベージコレクターによるオブジェクトの破棄は、厄介なアンマネージリソースを解放する絶好の機会です。これを行うには、Finalize()メソッドをオーバーライドします。

注: C#では、Finalize()メソッドを明示的にオーバーライドしません。あなたはその方法を書くようになりますC ++デストラクター、およびコンパイラは、それはあなたの実装であるために取るFinalize()方法を:

~MyObject()
{
    //we're being finalized (i.e. destroyed), call Dispose in case the user forgot to
    Dispose(); //<--Warning: subtle bug! Keep reading!
}

しかし、そのコードにはバグがあります。ご覧のとおり、ガベージコレクターはバックグラウンドスレッドで実行されます。2つのオブジェクトが破棄される順序はわかりません。あなたのDispose()コードでは、(あなたが助けになりたかったので)あなたが取り除こうとしているマネージドオブジェクトがもはやそこにない可能性があります:

public void Dispose()
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.gdiCursorBitmapStreamFileHandle);

   //Free managed resources too
   if (this.databaseConnection != null)
   {
      this.databaseConnection.Dispose(); //<-- crash, GC already destroyed it
      this.databaseConnection = null;
   }
   if (this.frameBufferImage != null)
   {
      this.frameBufferImage.Dispose(); //<-- crash, GC already destroyed it
      this.frameBufferImage = null;
   }
}

だから、何が必要のための方法であるFinalize()伝えるためにDispose()、それはすべきことをすべての管理は触れていない(彼らはので、リソースを使用できない可能性がありますまだアンマネージリソースを解放しながら、もう)。

これを行うための標準的なパターンが持つことであるFinalize()Dispose()の両方のコール第三(!)メソッド。Dispose()(とは対照的にFinalize())から呼び出しているかどうかを示すブール値を渡します。つまり、管理対象リソースを解放しても安全です。

この内部メソッドに、「CoreDispose」や「MyInternalDispose」などの任意の名前を付けることできますが、それを呼び出すのは慣習Dispose(Boolean)です。

protected void Dispose(Boolean disposing)

しかし、より有用なパラメータ名は次のとおりです。

protected void Dispose(Boolean itIsSafeToAlsoFreeManagedObjects)
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);

   //Free managed resources too, but only if I'm being called from Dispose
   //(If I'm being called from Finalize then the objects might not exist
   //anymore
   if (itIsSafeToAlsoFreeManagedObjects)  
   {    
      if (this.databaseConnection != null)
      {
         this.databaseConnection.Dispose();
         this.databaseConnection = null;
      }
      if (this.frameBufferImage != null)
      {
         this.frameBufferImage.Dispose();
         this.frameBufferImage = null;
      }
   }
}

そして、IDisposable.Dispose()メソッドの実装を次のように変更します。

public void Dispose()
{
   Dispose(true); //I am calling you from Dispose, it's safe
}

そしてあなたのファイナライザ:

~MyObject()
{
   Dispose(false); //I am *not* calling you from Dispose, it's *not* safe
}

:オブジェクトがを実装するオブジェクトの子孫である場合は、DisposeをオーバーライドするときにDisposeベースの Disposeメソッドを呼び出すことを忘れないでください。

public override void Dispose()
{
    try
    {
        Dispose(true); //true: safe to free managed resources
    }
    finally
    {
        base.Dispose();
    }
}

そして、すべてが良いです、 あなたがもっとうまくできることを除いて良いです!


ユーザーが電話した場合 Dispose()オブジェクトをと、すべてがクリーンアップされます。後で、ガベージコレクターがやって来てFinalizeを呼び出すと、Dispose再び呼び出されます。

これは無駄であるだけでなく、オブジェクトにすでに破棄されているオブジェクトへのジャンクリファレンスがある場合、 最後の呼び出しDispose()それらを再度破棄しようとします!

私のコードでは、破棄したオブジェクトへの参照を削除するように注意していたのでDispose、ジャンクオブジェクト参照を呼び出そうとしないでください。しかし、それでも微妙なバグが侵入するのを止めることはできませんでした。

ユーザーが呼び出すとDispose()、ハンドルCursorFileBitmapIconServiceHandleが破棄されます。後でガベージコレクターが実行されると、同じハンドルを再度破棄しようとします。

protected void Dispose(Boolean iAmBeingCalledFromDisposeAndNotFinalize)
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); //<--double destroy 
   ...
}

これを修正する方法は、ガベージコレクターにオブジェクトのファイナライズを煩わせる必要がないことを伝えることです。そのリソースはすでにクリーンアップされており、これ以上の作業は必要ありません。これを行うにGC.SuppressFinalize()は、Dispose()メソッドを呼び出します。

public void Dispose()
{
   Dispose(true); //I am calling you from Dispose, it's safe
   GC.SuppressFinalize(this); //Hey, GC: don't bother calling finalize later
}

ユーザーがを呼び出したDispose()ので、次のようになります。

  • 解放されたアンマネージリソース
  • 解放された管理リソース

GCでファイナライザを実行しても意味がありません。すべてが処理されます。

Finalizeを使用して、管理されていないリソースをクリーンアップできませんか?

のドキュメントはObject.Finalize言う:

Finalizeメソッドは、オブジェクトが破棄される前に、現在のオブジェクトが保持しているアンマネージリソースに対してクリーンアップ操作を実行するために使用されます。

しかし、MSDNのドキュメントにも次のように書かれていIDisposable.Disposeます。

アンマネージリソースの解放、解放、またはリセットに関連するアプリケーション定義のタスクを実行します。

どっち?アンマネージリソースをクリーンアップする場所はどれですか。答えは:

それはあなたの選択です!しかし、選択してくださいDispose

管理されていないクリーンアップをファイナライザに配置することもできます。

~MyObject()
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);

   //A C# destructor automatically calls the destructor of its base class.
}

これの問題は、ガベージコレクターがいつオブジェクトをファイナライズするのかわからないことです。管理されていない、不要な、使用されていないネイティブリソースは、ガベージコレクターが最終的に実行されるまで残ります。次に、ファイナライザメソッドを呼び出します。アンマネージリソースをクリーンアップしています。Object.Finalizeのドキュメントはこれを指摘しています:

ファイナライザが実行される正確な時間は定義されていません。クラスのインスタンスのリソースを確実に解放するには、Closeメソッドを実装するか、IDisposable.Dispose実装を提供します。

これは、Dispose管理されていないリソースのクリーンアップに使用することの利点です。管理されていないリソースがいつクリーンアップされるかを知り、制御することができます。それらの破壊は「決定論的」です。


元の質問に答えるには、GCがメモリを解放することを決定したときではなく、今すぐメモリを解放しないのはなぜですか。現在、530 MBの内部画像を必要としないため、これを取り除く必要がある顔認識ソフトウェアを持っています。そうしないと、マシンはスワッピングの停止にグラインドします。

ボーナスリーディング

この回答のスタイルが好きな人は(理由を説明し、その方法が明らかになるので)、Don BoxのEssential COMの第1章を読むことをお勧めします。

35ページで、彼はバイナリオブジェクトの使用の問題を説明し、目の前でCOMを発明しています。COM の理由を理解したら、残りの300ページは明らかであり、Microsoftの実装の詳細を説明します。

オブジェクトやCOMを扱ったことのあるすべてのプログラマは、少なくとも最初の章を読むべきだと思います。それはこれまでで最も良い説明です。

追加ボーナス読書

Eric Lippert が知っているすべてが間違っているとき

したがって、正しいファイナライザを作成することは実際には非常に困難です私ができる最善のアドバイスは、を試さないことです


12
あなたはもっと良いことができます-DisposeでGC.SuppressFinalize()の呼び出しを追加する必要があります。
台座

55
@ダニエル・イアウィッカー:本当です。マイクロソフトは、Win32の使用を完全に停止し、抽象化が容易で、デバイスに依存しない.NET Framework呼び出しに固執することを望んでいます。その下にあるオペレーティングシステムをいじりたい場合は、あなたあなたがあなたがあなたがあなたがあなたがあなた自身の手であなたの人生を取っていることを知っていると思うからです。すべての.NETアプリがWindowsまたはデスクトップで実行されているわけではありません。
Ian Boyd

34
これは素晴らしい答えですが、標準の場合と、クラスが既にDisposeを実装しているベースクラスから派生している場合の最終的なコードリストからメリットがあると思います。たとえば、ここ(msdn.microsoft.com/en-us/library/aa720161%28v=vs.71%29.aspx)を読んだこともあり、既にDispose(ちょっと私はこれが初めてです)。
integra753 2012

5
@GregS、その他:一般的に私はへの参照を設定する気になりませんnull。まず第一に、それはあなたがそれらを作ることができないことを意味しますreadonly、そして第二に、あなたは非常に醜い!=nullチェックをする必要があります(サンプルコードのように)。フラグを付けることもできますが、disposed気にしない方が簡単です。.NET GCは、フィールドへの参照xx.Dispose()行を通過するまでに「使用済み」としてカウントされなくなるほど強力です。
ポージュ

7
あなたが言及したドンボックスの本の2ページ目で、彼は検索アルゴリズムのO(1)実装の例を使用しています。その詳細は「読者のための演習として残されています」。私は笑った。
wipは2013年

65

IDisposableusingステートメントを悪用し、管理オブジェクトの確定的なクリーンアップを行う簡単な方法を利用するためによく使用されます。

public class LoggingContext : IDisposable {
    public Finicky(string name) {
        Log.Write("Entering Log Context {0}", name);
        Log.Indent();
    }
    public void Dispose() {
        Log.Outdent();
    }

    public static void Main() {
        Log.Write("Some initial stuff.");
        try {
            using(new LoggingContext()) {
                Log.Write("Some stuff inside the context.");
                throw new Exception();
            }
        } catch {
            Log.Write("Man, that was a heavy exception caught from inside a child logging context!");
        } finally {
            Log.Write("Some final stuff.");
        }
    }
}

6
私は個人的にはそれが好きですが、フレームワークの設計ガイドラインにはあまり慣れていません。
mqp 2009

4
特に、例外処理、ロック、およびアンマネージリソースを使用するブロックと複雑な方法で混在している場合に、決定論的なスコープとスコープの構築/クリーンアップが容易になるため、適切な設計と見なします。言語はこれをファーストクラスの機能として提供します。
yfeldblum 2009

FDGで指定された規則には厳密には従いませんが、「usingステートメント」で使用するために必要となるため、パターンの有効な使用法です。
スコットドーマン、

2
Log.Outdentがスローしない限り、これに間違いはありません。
Daniel Earwicker、2009

1
例外の安全性のために「スコープ付きの動作」を取得する手段としてIDisposableおよび「using」を使用することは虐待的ですか?に対するさまざまな答えさまざまな人々がこの手法を好き/嫌いな理由についてもう少し詳しく説明します。それはやや物議を醸している。
ブライアン

44

Disposeパターンの目的は、マネージリソースとアンマネージリソースの両方をクリーンアップするメカニズムを提供することであり、それがいつ発生するかは、Disposeメソッドの呼び出し方法によって異なります。あなたの例では、Disposeを使用しても、実際には破棄に関連することは何も行われていません。リストをクリアしても、破棄されるコレクションには影響がないためです。同様に、変数をnullに設定する呼び出しも、GCに影響を与えません。

Disposeパターンの実装方法について詳しくは、この記事をご覧ください。ただし、基本的には次のようになります。

public class SimpleCleanup : IDisposable
{
    // some fields that require cleanup
    private SafeHandle handle;
    private bool disposed = false; // to detect redundant calls

    public SimpleCleanup()
    {
        this.handle = /*...*/;
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Dispose managed resources.
                if (handle != null)
                {
                    handle.Dispose();
                }
            }

            // Dispose unmanaged managed resources.

            disposed = true;
        }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

ここで最も重要なメソッドはDispose(bool)です。これは、実際には2つの異なる状況で実行されます。

  • disposing == true:メソッドは、ユーザーのコードによって直接または間接的に呼び出されました。管理対象リソースと管理対象外リソースを破棄できます。
  • 破棄== false:メソッドはランタイムによってファイナライザの内部から呼び出されているため、他のオブジェクトを参照しないでください。管理されていないリソースのみを破棄できます。

GCにクリーンアップを実行させるだけの問題は、GCが収集サイクルを実行するタイミングを実際に制御できないことです(GC.Collect()を呼び出すことはできますが、実際には呼び出さないでください)。したがって、リソースが残る可能性があります。必要以上に長く。Dispose()を呼び出しても、実際には収集サイクルが発生するわけではなく、GCがオブジェクトを収集/解放することもありません。使用されるリソースをより確定的にクリーンアップし、GCにこのクリーンアップがすでに実行されていることを伝える手段を提供するだけです。

IDisposableとdisposeパターンの要点は、すぐにメモリを解放することではありません。Disposeの呼び出しで実際にメモリがすぐに解放される可能性があるのは、dispose == falseシナリオを処理していて、管理されていないリソースを操作しているときだけです。マネージコードの場合、GCがコレクションサイクルを実行するまで、メモリは再利用されません。これは、ユーザーが実際に制御できないコレクションサイクルです(GC.Collect()を呼び出す以外は、これについては既に説明しましたが、あまりお勧めできません)。

.NETの文字列は管理されていないリソースを使用せず、IDisposableを実装していないため、シナリオは実際には有効ではありません。強制的に「クリーンアップ」する方法はありません。


4
ファイナライザの実装を忘れていませんか?
Budda

@ブッダ:いいえ、彼はSafeHandleを使用しています。デストラクタは必要ありません。
Henk Holterman、

9
Dispose()への複数の呼び出しのセーフティネットを追加するための+1。仕様によると、複数の呼び出しは安全である必要があります。これを実装できないMicrosoftクラスが多すぎると、ObjectDisposedExceptionが発生します。
ジェシーチザム2012

5
ただし、Dispose(bool disposing)はSimpleCleanupクラスの独自のメソッドであり、フレームワークから呼び出されることはありません。パラメータとして「true」を指定して呼び出すだけなので、「破棄」がfalseになることはありません。コードはMSDNのIDisposableの例と非常に似ていますが、@ Buddaが指摘しているように、ファイナライザがありません。ここでdisposing = falseを使用した呼び出しが行われます。
ヨーヨー

19

Disposeが呼び出された後は、オブジェクトのメソッドへの呼び出しはありません(オブジェクトはDisposeへのさらなる呼び出しを許容する必要があります)。したがって、質問の例はばかげています。Disposeが呼び出された場合、オブジェクト自体を破棄できます。したがって、ユーザーはそのオブジェクトへのすべての参照を破棄する(それらをnullに設定する)だけでよく、その内部のすべての関連オブジェクトは自動的にクリーンアップされます。

マネージド/アンマネージドに関する一般的な質問と他の回答での議論については、この質問に対するすべての回答は、アンマネージドリソースの定義から始める必要があると思います。

つまり、システムを状態にするために呼び出すことができる関数があり、その状態からシステムを戻すために呼び出すことができる別の関数があるということです。さて、典型的な例では、最初の例はファイルハンドルを返す関数で、2番目の例はへの呼び出しCloseHandleです。

しかし、これが鍵となります。これらは、一致する関数のペアである可能性があります。1つは状態を構築し、もう1つはそれを破壊します。状態が構築されているが、まだ分解されていない場合、リソースのインスタンスが存在します。破棄が適切なタイミングで行われるように調整する必要があります。リソースはCLRによって管理されていません。自動的に管理されるリソースタイプはメモリのみです。GCとスタックの2種類があります。値タイプはスタックによって(または参照タイプ内のライドをヒッチすることによって)管理され、参照タイプはGCによって管理されます。

これらの関数は、自由にインターリーブできる状態の変化を引き起こしたり、完全に入れ子にする必要がある場合があります。状態変化はスレッドセーフかもしれませんし、そうでないかもしれません。

正義の質問の例を見てください。ログファイルのインデントへの変更は、完全に入れ子にする必要があります。そうしないと、すべてがうまくいきません。また、スレッドセーフになる可能性も低いです。

ガベージコレクターに乗車して、管理されていないリソースをクリーンアップすることができます。ただし、状態変更関数がスレッドセーフであり、2つの状態のライフタイムが何らかの方法で重複する場合のみ。したがって、Justiceのリソースの例にはファイナライザがあってはなりません!それは誰にも役に立たないだろう。

これらの種類のリソースについてはIDisposable、ファイナライザなしでを実装できます。ファイナライザは完全にオプションです-必須です。これは、多くの本で見落とされているか、言及されていません。

次に、usingステートメントを使用して、それが確実にDispose呼び出されるようにする必要があります。これは基本的に、スタックでライドをヒッチハイクするようなものです(ファイナライザーがGCにあるように、usingあるように、スタックに対してです)。

不足している部分は、Disposeを手動で記述して、フィールドと基本クラスを呼び出す必要があることです。C ++ / CLIプログラマはそうする必要はありません。ほとんどの場合、コンパイラーがそれらを書き込みます。

完全に入れ子になっていてスレッドセーフではない状態の場合、私が好む代替案があります(他のものを除いて、IDisposableを回避すると、IDisposableを実装するすべてのクラスにファイナライザを追加することに抵抗できない人との議論の問題が回避されます)。 。

クラスを作成する代わりに、関数を作成します。関数はデリゲートを受け入れて、以下にコールバックします。

public static void Indented(this Log log, Action action)
{
    log.Indent();
    try
    {
        action();
    }
    finally
    {
        log.Outdent();
    }
}

そして、簡単な例は次のようになります:

Log.Write("Message at the top");
Log.Indented(() =>
{
    Log.Write("And this is indented");

    Log.Indented(() =>
    {
        Log.Write("This is even more indented");
    });
});
Log.Write("Back at the outermost level again");

渡されるラムダはコードブロックとして機能するため、独自の制御構造を作成して、 usingですが、呼び出し側がそれを悪用する危険がなくなります。リソースのクリーンアップに失敗する可能性はありません。

リソースが存続期間が重複している可能性がある種類のリソースの場合、この手法はあまり役に立ちません。リソースA、リソースBを構築し、リソースAを削除して、後でリソースBを終了できるようにする必要があるためです。それはできません。ユーザーにこのように完全にネストするように強制した場合。ただし、使用する必要がありますIDisposable(ただし、無料ではないスレッドセーフを実装していない限り、ファイナライザーはありません)。


re:「Disposeが呼び出された後は、オブジェクトのメソッドをこれ以上呼び出さないでください」。「すべき」は手術の言葉です。保留中の非同期アクションがある場合、オブジェクトが破棄された後にアクションが発生する可能性があります。ObjectDisposedExceptionを発生させます。
ジェシーチザム2012

管理されていないリソースがGCが理解できない状態をカプセル化するという考えに触れている私の答えはあなたのものだけではないようです。ただし、アンマネージリソースの重要な側面は、リソースを「所有する」オブジェクトが存在しない場合でも、状態をクリーンアップする必要がある1つ以上のエンティティが存在し続けることができることです。私の定義はどうですか?かなり似ていますが、「リソース」がもう少し名詞的になっていると思います(サービスが不要になったときの通知と引き換えに、外部オブジェクトによる動作の変更に対する「同意」です)
supercat

@supercat-興味があれば、上の答えを書いてから数日後に次の投稿を書いた。 smellegantcode.wordpress.com/2009/02/13/...
ダニエルエリカー

1
@DanielEarwicker:興味深い記事ですが、実際にはカバーしていないアンマネージリソースの少なくとも1つのタイプを考えることができます:長期間有効なオブジェクトからのイベントのサブスクリプション。イベントサブスクリプションは代替可能ですが、メモリが無制限であったとしても、それらの破棄に失敗するとコストがかかる可能性があります。たとえば、列挙中に変更を許可するコレクションの列挙子は、コレクションからの更新通知をサブスクライブする必要があり、コレクションはその有効期間中に何度も更新される場合があります。登録解除せずに列挙子が放棄された場合...
スーパーキャット2013

1
操作のペアenterexit、私はリソースをどのように考えるかの核心です。イベントの購読/購読解除は、問題なくそれに収まるはずです。直交/ fungible特性に関しては、メモリリークと実際には区別できません。(サブスクリプションはオブジェクトをリストに追加するだけなので、これは当然のことです。)
Daniel Earwicker

17

IDisposableを使用するシナリオ:アンマネージリソースのクリーンアップ、イベントのサブスクライブ解除、接続のクローズ

IDisposableの実装に使用するイディオム(スレッドセーフではない):

class MyClass : IDisposable {
    // ...

    #region IDisposable Members and Helpers
    private bool disposed = false;

    public void Dispose() {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing) {
        if (!this.disposed) {
            if (disposing) {
                // cleanup code goes here
            }
            disposed = true;
        }
    }

    ~MyClass() {
        Dispose(false);
    }
    #endregion
}

完全なパターンの説明は、msdn.microsoft.com
en

3
アンマネージリソースがない限り、ファイナライザを含めないでください。それでも、推奨される実装は、アンマネージリソースをSafeHandleでラップすることです。
デイブブラック

11

うん、そのコードは完全に冗長で不必要であり、ガベージコレクターが他の方法では実行できないことを実行することはありません(MyCollectionのインスタンスがスコープ外になると、つまり)。 .Clear()呼び出し。

編集への回答:並べ替え。私がこれをすると:

public void WasteMemory()
{
    var instance = new MyCollection(); // this one has no Dispose() method
    instance.FillItWithAMillionStrings();
}

// 1 million strings are in memory, but marked for reclamation by the GC

メモリ管理の目的では、機能的にはこれと同じです。

public void WasteMemory()
{
    var instance = new MyCollection(); // this one has your Dispose()
    instance.FillItWithAMillionStrings();
    instance.Dispose();
}

// 1 million strings are in memory, but marked for reclamation by the GC

この瞬間に本当に本当にメモリを解放する必要がある場合は、を呼び出しますGC.Collect()。ただし、ここでこれを行う理由はありません。必要なときにメモリが解放されます。


2
re:「メモリは必要なときに解放されます。」むしろ、「GCが必要だと判断したとき」と言います。メモリが本当に必要であるとGCが判断する前に、システムパフォーマンスの問題が発生する場合があります。今すぐ解放することは必須ではないかもしれませんが、役に立つかもしれません。
ジェシーチザム2012

1
コレクション内の参照をnullにすると、それによって参照されるアイテムのガベージコレクションが迅速化されるという、まれなケースがあります。たとえば、大きな配列が作成され、新しく作成された小さな項目への参照で満たされているが、その後非常に長い間必要ない場合、配列を破棄すると、次のレベル2 GCまでこれらの項目が保持される可能性があります。最初にゼロにすると、アイテムは次のレベル0またはレベル1のGCに適格になります。確かに、Large Object Heapに寿命の短い大きなオブジェクトがあると(とにかくデザインが嫌いですが)
不気味

1
...それらのアレイを破棄する前にそのようなアレイをゼロにすることで、GCへの影響を減らすことができます。
スーパーキャット2013

11

もし MyCollectionとにかくガベージコレクションされるは、廃棄する必要はありません。これを行うと、CPUが必要以上にチャーンされるだけでなく、ガベージコレクターがすでに実行した計算済みの分析が無効になる場合もあります。

私はIDisposable、スレッドが正しく管理されていないことや、管理されていないリソースを確認するために使用しています。

編集スコットのコメントに応じて:

GCパフォーマンスメトリックが影響を受けるのは、[sic] GC.Collect()が呼び出されたときだけです。

概念的には、GCはオブジェクト参照グラフのビューと、スレッドのスタックフレームからのグラフへのすべての参照を維持します。このヒープは非常に大きく、多くのメモリページにまたがることがあります。最適化として、GCは頻繁に変更される可能性が低いページの分析をキャッシュして、ページを不必要に再スキャンしないようにします。ページ内のデータが変更されると、GCはカーネルから通知を受信するため、ページがダーティであり、再スキャンが必要であることがわかります。コレクションがGen0にある場合、ページの他の項目も変更されている可能性がありますが、これはGen1とGen2ではあまりありません。事例として、これらのフックは、そのプラットフォームでSilverlightプラグインを機能させるためにGCをMacに移植したチームのMac OS Xでは利用できませんでした。

リソースの不必要な廃棄に対するもう1つのポイント:プロセスがアンロードされている状況を想像してください。プロセスがしばらく実行されていることも想像してみてください。そのプロセスのメモリページの多くがディスクにスワップされている可能性があります。少なくとも、L1またはL2キャッシュにはありません。このような状況では、アプリケーションがアンロードして、すべてのデータとコードページをメモリにスワップし、プロセスが終了したときにオペレーティングシステムによって解放されるリソースを「解放」する意味はありません。これは、管理されているリソースや特定の管理されていないリソースにも当てはまります。バックグラウンド以外のスレッドを存続させるリソースのみを破棄する必要があります。そうしないと、プロセスは存続します。

これで、通常の実行中に、アンマネージメモリリークを回避するために(@fezmonkeyがデータベース接続、ソケット、ウィンドウハンドルを指摘するため)正しくクリーンアップする必要がある一時的なリソースがあります。これらは処分しなければならない種類のものです。スレッドを所有するクラスを作成した場合(所有者はスレッドを作成したため、少なくとも私のコーディングスタイルによって確実に停止する必要があります)、そのクラスIDisposableは中にスレッドを実装して破棄する必要がありますDispose

.NETフレームワークはIDisposable、このクラスを破棄する必要があることを開発者への警告としても、シグナルとしてインターフェイスを使用します。IDisposable実装がオプションである場合(明示的なインターフェース実装を除く)を実装するフレームワークのタイプは考えられません。


Disposeの呼び出しは完全に有効で合法であり、推奨されています。IDisposableを実装するオブジェクトは通常、理由によりそうします。GCパフォーマンスメトリックが影響を受けるのは、GC.Collect()が呼び出されたときだけです。
スコットドーマン

多くの.netクラスでは、廃棄は「ある程度」オプションです。つまり、インスタンスを放棄しても、新しいインスタンスの作成と放棄に夢中にならない限り、問題は発生しません。たとえば、コントロール用にコンパイラが生成したコードは、コントロールがインスタンス化されるとフォントを作成し、フォームが破棄されるとフォントを破棄するように見えます。何千ものコントロールを作成して破棄すると、何千ものGDIハンドルが占有される可能性がありますが、ほとんどの場合、コントロールはそれほど作成および破棄されません。それにもかかわらず、そのような放棄を避けるように努めるべきです。
スーパーキャット、2011

1
フォントの場合、問題は、Microsoftがコントロールに割り当てられた「フォント」オブジェクトの破棄を担当するエンティティを実際に定義したことがないことだと思います。場合によっては、コントロールがフォントをより長期間有効なオブジェクトと共有することがあります。そのため、コントロールにフォントを破棄させると悪い結果を招きます。他の場合では、フォントはコントロールに割り当てられ、他の場所には割り当てられないため、コントロールがフォントを破棄しない場合、誰もそれを行いません。ちなみに、コントロールがフォントのGDIハンドルを使用していないように見えるため、個別の使い捨てできないFontTemplateクラスがあれば、フォントに関するこの問題は回避できたはずです。
スーパーキャット、2011

オプションのトピックをDispose()呼び出し、以下を参照してくださいstackoverflow.com/questions/913228/...
RJカスバートソン

7

あなたが投稿した例では、それでも「今すぐメモリを解放する」ことはできません。すべてのメモリはガベージコレクションされますが、以前の世代でメモリを収集できる場合があります。確かにいくつかのテストを実行する必要があります。


フレームワーク設計ガイドラインはガイドラインであり、ルールではありません。それらは、インターフェースの主な目的、それをいつ使用するか、どのように使用するか、いつ使用しないかを示します。

私はかつて、IDisposableを利用して失敗したときに単純なRollBack()であったコードを読みました。以下のMiniTxクラスはDispose()のフラグをチェックし、Commit呼び出しが発生しなかった場合はそれRollback自体を呼び出します。間接層が追加され、呼び出し元のコードの理解と維持がはるかに容易になりました。結果は次のようになります。

using( MiniTx tx = new MiniTx() )
{
    // code that might not work.

    tx.Commit();
} 

タイミング/ロギングコードが同じことをするのを見たことがあります。この場合、Dispose()メソッドはタイマーを停止し、ブロックが終了したことをログに記録しました。

using( LogTimer log = new LogTimer("MyCategory", "Some message") )
{
    // code to time...
}

そこで、アンマネージリソースのクリーンアップは行わないが、IDisposableを使用してよりクリーンなコードを作成する具体的な例をいくつか示します。


高次関数を使用した@Daniel Earwickerの例を見てください。ベンチマーク、タイミング、ロギングなどについては、はるかに簡単です。
Aluan Haddad


6

管理されていないリソースの使用または解放に関する通常のことは繰り返さないで、すべてカバーしました。しかし、私はよくある誤解のように思われるものを指摘したいと思います。
次のコードを考える

パブリッククラスLargeStuff
  IDisposableを実装します
  string()としてのプライベート_Large

  '_Largeを意味するいくつかの奇妙なコードには、数百万の長い文字列が含まれています。

  Public Sub Dispose()はIDisposable.Disposeを実装します
    _Large =なし
  End Sub

Disposableの実装は現在のガイドラインに従っていないことを私は理解していますが、うまくいけばみなさんはこのアイデアを理解できます。
今、Disposeが呼び出されると、どのくらいのメモリが解放されますか?

回答:なし。
Disposeを呼び出すと、アンマネージリソースを解放できます。マネージメモリを解放することはできません。GCだけがそれを実行できます。上記が良い考えではないということではありません。上記のパターンに従うことは、実際には依然として良い考えです。Disposeが実行されると、LargeStuffのインスタンスがまだスコープ内にある場合でも、_Largeによって使用されていたメモリの再利用をGCが停止することはありません。_Largeの文字列もgen 0にある可能性がありますが、LargeStuffのインスタンスはgen 2である可能性があるため、メモリはより早く再利用されます。
ただし、上記のDisposeメソッドを呼び出すファイナライザーを追加しても意味がありません。これは、ファイナライザが実行できるようにメモリの再利用を遅らせるだけです。


1
のインスタンスがLargeStuffジェネレーション2に到達するのに十分な長_Largeさであり、ジェネレーション0にある新しく作成された文字列への参照を保持している場合、のインスタンスがnullにならLargeStuffずに破棄さ_Largeれると、_Large次のGen2コレクションまで保持されます。ゼロに設定する_Largeと、次のGen0コレクションで文字列が削除される可能性があります。ほとんどの場合、参照をゼロにすることは役に立ちませんが、いくつかの利点を提供できる場合があります。
スーパーキャット2013年

5

別に制御する方法として、その主な用途から生涯システムリソースを(完全に素晴らしい答えでカバーイアン、栄光を!)、IDisposableを/使用してコンボすることも使用することができますスコープ(クリティカル)グローバルリソースの状態変化コンソールスレッドプロセス、任意のグローバル・オブジェクトのようなアプリケーションインスタンス

このパターンに関する記事を書きました:http : //pragmateek.com/c-scope-your-global-state-changes-with-idisposable-and-the-using-statement/

これは、頻繁に使用されるグローバル状態を再利用可能読みやすい方法で保護する方法を示しています。コンソールの色、現在のスレッドカルチャExcelアプリケーションオブジェクトのプロパティ ...


4

どちらかと言えば、コードを省略した場合よりもコードの効率が悪くなると思います。

Clear()メソッドの呼び出しは不要であり、GCはDisposeがそれを行わなかった場合、おそらくそれを行いません...


2

オブジェクトの通常のGCが原因で発生しない影響が生じる可能性があるDispose()、サンプルコードでの操作の動作があります。 MyCollection

他のオブジェクトが参照_theListまたは_theDict参照しているオブジェクトは、そのオブジェクトList<>またはDictionary<>オブジェクトはコレクションの対象にはなりませんが、突然コンテンツがなくなります。例のようにDispose()操作がない場合でも、これらのコレクションにはコンテンツが含まれています。

これは私が壊れたデザイン、それを呼ぶような状況であればもちろん、 -私はちょうどこと(pedantically、私は仮定)を指摘していますDispose()操作は、他の用途があるかどうかに応じて、完全に冗長ではないかもしれませんList<>か、Dictionary<>それはありませんが、フラグメントに示されています。


それらはプライベートフィールドなので、OPがそれらへの参照を提供していないと想定するのは公平だと思います。
mqp 2009

1)コードフラグメントは単なるコード例であるため、見落としやすい副作用がある可能性があることを指摘しています。2)プライベートフィールドは、ゲッタープロパティ/メソッドのターゲットであることがよくあります-多すぎる(ゲッター/セッターは、一部の人々によって少しアンチパターンであると考えられています)。
マイケル・バー、

2

「アンマネージリソース」に関するほとんどの議論の1つの問題は、それらが実際には用語を定義していないが、アンマネージコードと関係があることを暗示しているように見えることです。多くの種類のアンマネージリソースがアンマネージコードとやり取りすることは事実ですが、アンマネージリソースをそのような用語で考えることは役に立ちません。

代わりに、すべての管理対象リソースに共通するものを認識する必要があります。これらはすべて、オブジェクトの代わりに外部の「何か」に何かを依頼して、他のいくつかの「もの」を損ない、他のエンティティがそうすることに同意することを必要とします追っての通知。オブジェクトが跡形もなく放棄されて消滅する場合、存在しなくなったオブジェクトに代わって動作を変更する必要がなくなったことを外部の「もの」に伝えることはできません。その結果、「もの」の有用性は永久に低下します。

管理されていないリソースは、オブジェクトに代わって動作を変更するための外部の「もの」による合意を表します。オブジェクトが放棄されて存在しなくなった場合、その外部の「もの」の有用性を損なうことになります。管理対象リソースは、そのような契約の受益者であるオブジェクトですが、破棄された場合に通知を受信するようにサインアップし、破棄される前にそのような通知を使用してその順序を整えます。


まあ、IMO、アンマネージオブジェクトの定義は明確です。非GCオブジェクト
Eonil、2014年

1
@Eonil:アンマネージオブジェクト!=アンマネージリソース。イベントなどは完全にマネージオブジェクトを使用して実装できますが、アンマネージリソースを構成します。少なくとも短期オブジェクトが長期オブジェクトのイベントにサブスクライブしている場合、GCはそれらをクリーンアップする方法について何も知らないためです。 。
スーパーキャット2014年


2

最初の定義。私にとって管理されていないリソースとは、IDisposableインターフェイスまたはDLLの呼び出しを使用して作成されたものを実装するクラスを意味します。GCは、そのようなオブジェクトを処理する方法を知りません。クラスにたとえば値型のみがある場合、このクラスはアンマネージリソースを持つクラスとは見なしません。私のコードでは、次のプラクティスに従います。

  1. 私が作成したクラスがいくつかのアンマネージリソースを使用している場合、メモリをクリーンアップするためにIDisposableインターフェイスも実装する必要があることを意味します。
  2. 使い終わったらすぐに掃除してください。
  3. 私のdisposeメソッドでは、クラスのすべてのIDisposableメンバーを反復処理し、Disposeを呼び出します。
  4. 私のDisposeメソッドでは、GC.SuppressFinalize(this)を呼び出して、オブジェクトが既にクリーンアップされていることをガベージコレクターに通知します。GCの呼び出しは高コストな操作なので行います。
  5. 追加の予防策として、Dispose()を複数回呼び出せるようにしています。
  6. プライベートメンバー_disposedを追加して、メソッド呼び出しをチェックインすると、オブジェクトがクリーンアップされたことがあります。そして、クリーンアップされている場合は、ObjectDisposedException を生成します。
    次のテンプレートは、コードのサンプルとして言葉で説明したことを示しています。

public class SomeClass : IDisposable
    {
        /// <summary>
        /// As usually I don't care was object disposed or not
        /// </summary>
        public void SomeMethod()
        {
            if (_disposed)
                throw new ObjectDisposedException("SomeClass instance been disposed");
        }

        public void Dispose()
        {
            Dispose(true);
        }

        private bool _disposed;

        protected virtual void Dispose(bool disposing)
        {
            if (_disposed)
                return;
            if (disposing)//we are in the first call
            {
            }
            _disposed = true;
        }
    }

1
「私にとってアンマネージリソースとは、IDisposableインターフェイスまたはDLLの呼び出しを使用して作成された何かを実装するクラスを意味します。」それでis IDisposable、それ自体が管理されていないリソースと見なされるべきであるタイプがあると言っていますか?それは正しくないようです。また、実装型が純粋な値型の場合、破棄する必要がないことを示唆しているようです。それも間違っているようです。
Aluan Haddad

誰もが自分で判断します。私はコードを追加するためだけにコードを追加したくありません。これは、IDisposableを追加した場合、GCが管理できない何らかの機能を作成したか、GCのライフタイムを適切に管理できないことを意味します。
Yuriy Zaletskyy

2

あなたの与えられたコードサンプルは、IDisposable使い方の良い例ではありません。辞書のクリアは通常Disposeメソッドに送るべきではありません。辞書の項目は、範囲外になると消去されて破棄されます。IDisposableスコープ外になっても解放/解放されないメモリ/ハンドラを解放するには、実装が必要です。

次の例は、いくつかのコードとコメントを含むIDisposableパターンの良い例を示しています。

public class DisposeExample
{
    // A base class that implements IDisposable. 
    // By implementing IDisposable, you are announcing that 
    // instances of this type allocate scarce resources. 
    public class MyResource: IDisposable
    {
        // Pointer to an external unmanaged resource. 
        private IntPtr handle;
        // Other managed resource this class uses. 
        private Component component = new Component();
        // Track whether Dispose has been called. 
        private bool disposed = false;

        // The class constructor. 
        public MyResource(IntPtr handle)
        {
            this.handle = handle;
        }

        // Implement IDisposable. 
        // Do not make this method virtual. 
        // A derived class should not be able to override this method. 
        public void Dispose()
        {
            Dispose(true);
            // This object will be cleaned up by the Dispose method. 
            // Therefore, you should call GC.SupressFinalize to 
            // take this object off the finalization queue 
            // and prevent finalization code for this object 
            // from executing a second time.
            GC.SuppressFinalize(this);
        }

        // Dispose(bool disposing) executes in two distinct scenarios. 
        // If disposing equals true, the method has been called directly 
        // or indirectly by a user's code. Managed and unmanaged resources 
        // can be disposed. 
        // If disposing equals false, the method has been called by the 
        // runtime from inside the finalizer and you should not reference 
        // other objects. Only unmanaged resources can be disposed. 
        protected virtual void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called. 
            if(!this.disposed)
            {
                // If disposing equals true, dispose all managed 
                // and unmanaged resources. 
                if(disposing)
                {
                    // Dispose managed resources.
                    component.Dispose();
                }

                // Call the appropriate methods to clean up 
                // unmanaged resources here. 
                // If disposing is false, 
                // only the following code is executed.
                CloseHandle(handle);
                handle = IntPtr.Zero;

                // Note disposing has been done.
                disposed = true;

            }
        }

        // Use interop to call the method necessary 
        // to clean up the unmanaged resource.
        [System.Runtime.InteropServices.DllImport("Kernel32")]
        private extern static Boolean CloseHandle(IntPtr handle);

        // Use C# destructor syntax for finalization code. 
        // This destructor will run only if the Dispose method 
        // does not get called. 
        // It gives your base class the opportunity to finalize. 
        // Do not provide destructors in types derived from this class.
        ~MyResource()
        {
            // Do not re-create Dispose clean-up code here. 
            // Calling Dispose(false) is optimal in terms of 
            // readability and maintainability.
            Dispose(false);
        }
    }
    public static void Main()
    {
        // Insert code here to create 
        // and use the MyResource object.
    }
}

1

管理されたリソースの破棄の最も正当な使用例は、GCが他の方法では収集されないリソースを再利用するための準備です。

主な例は循環参照です。

循環参照を回避するパターンを使用するのがベストプラクティスですが、(たとえば)「親」への参照を持つ「子」オブジェクトで終わる場合、単に放棄すると親のGCコレクションが停止する可能性があります。リファレンスとGCに依存-さらに、ファイナライザを実装している場合は呼び出されません。

これを回避する唯一の方法は、子の親参照をnullに設定して、循環参照を手動で解除することです。

これを行うには、親と子にIDisposableを実装するのが最善の方法です。親でDisposeが呼び出されたら、すべての子でDisposeを呼び出し、子のDisposeメソッドで、親の参照をnullに設定します。


4
ほとんどの場合、GCは死んだオブジェクトを特定するのではなく、生きているオブジェクトを特定することで機能します。各GCサイクルの後、ファイナライズに登録された各オブジェクトについて、ラージオブジェクトヒープに格納されるか、ライブのターゲットであるWeakReference場合、システムは、最後のGCサイクルでライブルート参照が見つかったことを示すフラグをチェックします、そして即時ファイナライズが必要なオブジェクトのキューにオブジェクトを追加するか、ラージオブジェクトヒープからオブジェクトを解放するか、弱い参照を無効にします。他の参照が存在しない場合、循環参照はオブジェクトを存続させません。
スーパーキャット2016

1

管理されたリソースと管理されていないリソースの両方でIDisposableを使用することについて話すために、多くの答えがシフトしているのがわかります。この記事は、IDisposableを実際に使用する方法について私が見つけた最も良い説明の1つとしてお勧めします。

https://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About

実際の質問については; IDisposableを使用して、大量のメモリを消費している管理対象オブジェクトをクリーンアップすると、短い答えはノーになります。その理由は、いったんIDisposableを破棄すると、それをスコープ外に出す必要があるからです。その時点で、参照されている子オブジェクトもスコープ外にあり、収集されます。

これに対する唯一の本当の例外は、管理対象オブジェクトで大量のメモリが占​​有されていて、何らかの操作が完了するのを待つそのスレッドをブロックした場合です。その呼び出しが完了した後に不要になるオブジェクトがある場合、それらの参照をnullに設定すると、ガベージコレクターがオブジェクトをより早く収集できるようになる場合があります。しかし、そのシナリオは、リファクタリングする必要のある悪いコードを表すでしょう-IDisposableのユースケースではありません。


1
なぜ誰かがあなたの答えに-1を付けたのか理解できませんでした
セバスチャンオスカーロペス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.