66

次に示すのは、典型的な破棄パターンの例です。

 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) を呼び出す理由がわかりません。定義を見ると、まったく何もしないのに、なぜこのようなコードを書くのでしょうか? デストラクタから dispose をまったく呼び出さないのは理にかなっているでしょうか?

4

6 に答える 6

51

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

これは、クリーンアップするリソースを管理および非管理しているクラスのMSDNの例です。

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

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);
    }
}
于 2009-03-10T03:11:24.283 に答える
19
于 2009-03-10T02:54:09.723 に答える
9

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

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


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

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

と同等です

protected override void Finalize()
{
    try
    {
        // Cleanup statements...
    }
    finally
    {
        base.Finalize();
    }
}
于 2009-03-10T02:53:25.153 に答える
3

混乱は、あなたの例では管理されていないリソースを解放していないという事実によるものだと思います。これらは、ガベージコレクションを介してdisposeが呼び出されたときにも解放する必要があり、のチェックのdisposingで解放されます。管理されていないリソースの解放に関する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);
   }
于 2009-03-10T03:10:42.913 に答える
2

if(disposing) 内では、管理されていないリソース (データベース接続など) を持つ管理対象オブジェクトに対して dispose/close を呼び出すことになっています。それらに対して dispose を呼び出す必要があります。また、ファイナライズの順序は未定であるため、既に破棄されたオブジェクトに対して dispose を呼び出している可能性があります。

于 2009-03-10T02:59:48.197 に答える