web-dev-qa-db-ja.com

破棄します、いつ呼び出されますか?

次のコードを検討してください。

_namespace DisposeTest
{
    using System;

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Calling Test");

            Test();

            Console.WriteLine("Call to Test done");
        }

        static void Test()
        {
            DisposeImplementation di = new DisposeImplementation();
        }
    }

    internal class DisposeImplementation : IDisposable
    {
        ~DisposeImplementation()
        {
            Console.WriteLine("~ in DisposeImplementation instance called");
        }
        public void Dispose()
        {
            Console.WriteLine("Dispose in DisposeImplementation instance called");
        }
    }
}
_

たとえTest();呼び出しの後に待機ループを置いても、Disposeが呼び出されることはありません。つまり、それはかなり悪いことです。考えられるすべてのリソースが確実にクリーンアップされるように、簡単で非常に使いやすいクラスを作成したいと考えています。クラスのユーザーにその責任を負わせたくありません。

可能な解決策:usingを使用するか、Dispose myselfを呼び出します(基本的に同じです)。ユーザーに使用を強制することはできますか?または、破棄を強制的に呼び出すことはできますか?

GC.Collect();の後にTest();を呼び出しても機能しません。

dinullに入れても、Disposeは呼び出されません。 Deconstructorは機能するので、オブジェクトは終了時に分解されますTest()

わかりました、今ははっきりしています!

ご回答ありがとうございます!コメントに警告を追加します!

32
Snake

考えられるすべてのリソースが確実にクリーンアップされるように、簡単で非常に使いやすいクラスを作成したいと考えています。クラスのユーザーにその責任を負わせたくありません。

それはできません。メモリ管理は、特にメモリではないリソースに対応するように構築されていないだけです。

IDisposableパターンは、参照カウントなどを使用してメモリ管理がそれを理解しようとするのではなく、オブジェクトが使用されたときにオブジェクトを通知する方法として開発者を対象としています。

Finalizerは、オブジェクトを適切に破棄できないユーザーのフォールバックとして使用できますが、オブジェクトをクリーンアップするための主要な方法としてはうまく機能しません。円滑に作業するには、オブジェクトを適切に配置する必要があります。これにより、よりコストのかかるFinalizerを呼び出す必要がなくなります。

20
Guffa

OPの質問に対処するには、いくつかの重要なポイントを作成する必要があります。

  1. .NET GCは非決定的です(つまり、いつ発生するかわからず、依存する必要もありません)。
  2. Disposeが.NET Frameworkによって呼び出されることはありません。手動で呼び出す必要があります。できれば、その作成をusing()ブロックでラップしてください。
  3. Dispose()を呼び出さずに使い捨てオブジェクトを明示的にnullに設定することは悪いことです。発生するのは、オブジェクトの「ルート参照」を明示的にnullに設定したことです。つまり、後でDisposeを呼び出すことはできません。さらに重要なのは、オブジェクトをGCファイナライズキューに送信してファイナライズすることです。プログラミングの悪い習慣によってファイナライズを引き起こすことは、いかなる犠牲を払っても避けられるべきです。

Finalizer:一部の開発者はこれをデストラクタと呼んでいます。 そして実際、それは C#4.0言語仕様(セクション1.6.7.6) およびprevious現在のバージョン ECMA-334仕様。幸い、第4版(2006年6月)では、セクション8.7.9でファイナライザを正しく定義し、セクション17.12で2つの間の混乱を解消しようとしています。 .NET Frameworkの従来のデストラクターとデストラクター/ファイナライザーとして知られているものの間には、重要な内部の違い(ここでこれらの細かい詳細に入る必要はありません)があることに注意してください。

  1. ファイナライザが存在する場合、GC.SuppressFinalize()が呼び出されない場合にのみ、.NET Frameworkによってファイナライザが呼び出されます。
  2. ファイナライザを明示的に呼び出さないでください。幸い、C#はこれを明示的に許可しません(他の言語については知りません)。ただし、GCの第2世代でGC.Collect(2)を呼び出すことで強制できます。

ファイナライズ:ファイナライズは、「正常な」クリーンアップとリソースの解放を処理する.NET Frameworkの方法です。

  1. ファイナライズキューにオブジェクトがある場合にのみ発生します。
  2. これは、Gen2のガベージコレクションが発生した場合にのみ発生します(適切に作成された.NETアプリの100コレクションごとに1)。
  3. .NET 4までは、単一のFinalizationスレッドがあります。何らかの理由でこのスレッドがブロックされた場合、アプリはねじ込まれます。
  4. 適切で安全なファイナライズコードを書くことは簡単ではなく、間違いが非常に簡単に起こります(つまり、ファイナライザから例外が誤ってスローされることを許可し、既にファイナライズされている可能性がある他のオブジェクトへの依存を許可するなど)。

これは確かにあなたが求めたより多くの情報ですが、それは物事がどのように機能するか、なぜ彼らが彼らのやり方で機能するのかについての背景を提供します。一部の人々は、.NETでのメモリとリソースの管理について心配する必要はないと主張しますが、それによって、実行する必要があるという事実が変わることはありません。また、近い将来にそうなるとは思わないでしょう。

46
Dave Black

すべての答えは(多かれ少なかれ)正しいです、ここに例があります:

_static void Test()
{
    using (DisposeImplementation di = new DisposeImplementation())
    {
        // Do stuff with di
    }
}
_

Disposeを手動で呼び出すこともできますが、usingステートメントの利点は、例外がスローされるため、制御ブロックを離れるとオブジェクトも破棄されることです。

IDisposableインターフェイスの使用を「忘れた」場合に備えて、破棄するリソースを処理するファイナライザを追加できます。

_public class DisposeImplementation : IDisposable
{    
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // get rid of managed resources
        }   
        // get rid of unmanaged resources
    }

    ~DisposeImplementation()
    {
        Dispose(false);
    }
}
_

詳細は この質問 を参照してください。ただし、これはクラスを正しく使用していない人々を補うためのものです:)開発者に間違いを警告するために、Finalizerに大きなfat Debug.Fail()呼び出しを追加することをお勧めします。

パターンの実装を選択すると、GC.Collect()が破棄をトリガーすることがわかります。

13
Thorarin

これをクラスのパターン/テンプレートとして使用します

_public class MyClass : IDisposable
{
    private bool disposed = false;

    // 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.
    private 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.                
                ......
            }

            // Call the appropriate methods to clean up
            // unmanaged resources here.
            // If disposing is false,
            // only the following code is executed.
            ...........................

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

    // 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.
    ~MyClass()
    {
        // Do not re-create Dispose clean-up code here.
        // Calling Dispose(false) is optimal in terms of
        // readability and maintainability.
        Dispose(false);
    }
}
_

そしてもちろん、他の人が述べたようにusing(...){}ブロックを忘れないでください。

7
Incognito

Disposeを明示的に呼び出すか、オブジェクトをusingステートメントでラップする必要があります。例:

using (var di = new DisposeImplementation())
{
}

可能な解決策:usingを使用するか、Dispose myselfを呼び出します(基本的に同じ)。

usingを使用することは、Disposeブロック内でfinallyを呼び出すことと同じです。

2
Anne Sharp

Disposeメソッドを呼び出すか、usingを使用して、自分で破棄することになっています。覚えておいてください、それはデコンストラクタではありません!

クラスのユーザーがリソースを適切に破棄することを信頼できない場合、おそらくユーザーは他の方法で混乱するでしょう。

1
Hans Olsson

Disposeは自動的に呼び出されません。 using句を使用して使用法をラップするか、手動で呼び出す必要があります。

参照 http://msdn.Microsoft.com/en-us/library/aa664736%28VS.71%29.aspx

デストラクタからdisposeを呼び出せない...プロジェクトでこれを少し前に試しました。

1
Rodrick Chapman