web-dev-qa-db-ja.com

なぜデストラクターでdispose(false)を呼び出すのですか?

次に、典型的な廃棄パターンの例を示します。

 public bool IsDisposed { get; private set; }

  #region IDisposable Members

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

  protected virtual void Dispose(bool disposing)
  {
    if (!IsDisposed)
    {
      if (disposing)
      {
        //perform cleanup here
      }

      IsDisposed = true;
    }
  }

  ~MyObject()
  {
    Dispose(false);
  }

私はdisposeが何をするのか理解していますが、なぜデストラクタでdispose(false)を呼び出したいのか理解できないのですか?定義を見ると、まったく何もしないのに、なぜこのようなコードを書くのでしょうか? notデストラクターからdisposeを呼び出すだけでも意味がありませんか?

60
ryeguy

オブジェクトが何らかの理由で適切に破棄されない場合、ファイナライザーはフォールバックとして使用されます。通常、ファイナライザフックアップを削除し、オブジェクトをガベージコレクタが簡単に削除できる通常の管理対象オブジェクトに変換するDispose()メソッドが呼び出されます。

これは、クリーンアップするための管理対象および管理対象外のリソースを含むクラスのMSDNの例です。

マネージリソースはdisposingがtrueの場合にのみクリーンアップされますが、アンマネージリソースは常にクリーンアップされることに注意してください。

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.
    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.
                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);
    }
}
44
Guffa

「ここでの考え方は、Dispose(Boolean)が明示的なクリーンアップを実行するために呼び出されるかどうか(ブール値がtrue)とガベージコレクションのために呼び出されるかどうか(ブール値がfalse)を知ることです。明示的に、Dispose(Boolean)メソッドは、他のオブジェクトがまだファイナライズまたは破棄されていないことを確認して、他のオブジェクトを参照する参照型フィールドを使用して安全にコードを実行できます。これらのオブジェクトは既にファイナライズされている可能性があるため、参照型フィールドを参照するコードを実行しないでください。」

「破棄、ファイナライズ、およびリソース管理の設計ガイドライン」 にはさらに多くの情報があります。

編集:リンク。

18
stuartd

C#にはデストラクタはありません。それはファイナライザーです。これは別のことです。

区別は、管理対象オブジェクトをクリーンアップする必要があるかどうかです。それらはファイナライザ自体でファイナライズされている可能性があるため、ファイナライザでそれらをクリーンアップしようとするのは望ましくありません。


私はつい最近、C#プログラミングガイドの Destructors ページを見ました。上記の答えが間違っていたことを示しています。特に、デストラクタとファイナライザには違いがあります。

class Car
{
    ~Car()  // destructor
    {
        // cleanup statements...
    }
}

に等しい

protected override void Finalize()
{
    try
    {
        // Cleanup statements...
    }
    finally
    {
        base.Finalize();
    }
}
9
John Saunders

この混乱は、あなたの例では管理されていないリソースを解放していないという事実によると思います。これらは、ガベージコレクションを介してdisposeが呼び出されたときにも解放する必要があり、解放されますoutsidedisposingのチェック。 アンマネージリソースの解放 に関するMSDNの例を参照してください。チェックの外で発生する/発生するはずのもう1つは、基本クラスのDisposeメソッドの呼び出しです。

引用された記事から:

   protected override void Dispose(bool disposing) 
   {
      if (disposing) 
      {
         // Release managed resources.
      }
      // Release unmanaged resources.
      // Set large fields to null.
      // Call Dispose on your base class.
      base.Dispose(disposing);
   }
3
tvanfosson

If(disposing)内では、アンマネージリソース(データベース接続など)を持つマネージオブジェクトでdispose/closeを呼び出すことになっています。ファイナライザーが呼び出されると、これらのオブジェクトに到達できなくなるため、オブジェクト自体をファイナライズできますが、それらを破棄する必要があります。また、ファイナライズの順序は決まっていないため、すでに破棄されたオブジェクトでdisposeを呼び出すことができます。

1
ggf31416

次の例は、IDisposableインターフェイスを実装するリソースクラスを作成する方法を示しています。 https://msdn.Microsoft.com/en-us/library/System.IDisposable.aspx

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