2009-03-10 20 views
50

どのような次は典型的な処分のパターンの例である:デストラクタで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); 
    } 

私はない処分か理解していますが、デストラクタで処分(false)を呼び出したいでしょうなぜ私が理解していないことはありますか?あなたが定義を見れば、それは絶対に何もしないでしょう。だから、誰がこのようなコードを書くのですか? ではなく、をデストラクタから呼び出すことは全く意味がありませんか?

答えて

38

ファイナライザは、フォールバックとして使用されています。通常は、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); 
    } 
} 
+16

しかし、あなたがアンマネージドリソースを持っていなければ 'Dispose(false)'はまったく何もしないので、ファイナライザも 'Dispose(bool)'も必要ないことに注意してください。私は、標準パターンは、ほとんど発生しないユースケースに対応するために過度に複雑であると感じています。ここで私が大好きなのは、http://nitoprograms.blogspot.com/2009/08/how-to-implement-idisposable-and.html –

+0

@romkyns "[IDisposable]の主な用途は、アンマネージドリソースを解放することです。 (http://msdn.microsoft.com/en-us/library/System.IDisposable.aspx)IDisposableを実装する標準的な方法は、管理されていないリソースを持たない場合に必要な以上に驚くことではありません。 「ほとんど発生しないユースケース」が何を意味するのかよく分かりません。管理対象リソースとアンマネージリソースを混在させることはあいまいなユースケースではありません。 –

+0

それは、ファイナライザが何もしていない(管理されていないリソースがないので)なら、あなたはそれを必要としないと私は同意します。ほとんどのパターン(ほとんどのもののように、私が推測する)のように、それは理にかなっているところで使うのが理にかなっています。 ;) –

8

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

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


私は最近C#プログラミングガイドのDestructorsページを見て起こりました。それは私が私の答えで間違っていたことを示しています。具体的には、デストラクタとファイナライザとの間に差がある:

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

protected override void Finalize() 
{ 
    try 
    { 
     // Cleanup statements... 
    } 
    finally 
    { 
     base.Finalize(); 
    } 
} 
+0

なぜ、すべてを一緒に電話を省略しないのですか? – ryeguy

+0

あなたはまだ未管理のリソースをクリーンアップする可能性があります。 – Chris

+0

しかし、処分はIfループ内で行われます。渡されたパラメータがFalse(ファイナライザから)のときには実行されません – ryeguy

16

に相当し、「ここでの考え方は、 廃棄(ブール値)は、それが がに呼び出されているかどうかを知っているということです、なぜならとき01によるガベージコレクション (ブール値がfalse)に呼ば さ対明示的なクリーンアップ (ブール値がtrueである)。この 区別は便利ですかが明示的に配置され、 廃棄(Boolean)メソッドを安全 参照型にこれらの他の オブジェクトがファイナライズされていないか、 まだ処分することを確実に知る他のオブジェクト を参照 フィールドを使用してコードを実行することができます。ブール falseの場合、それらの オブジェクトがすでに が確定されている可能性があるため、廃棄(Boolean)メソッド は 参照型のフィールドを参照するコードを実行するべきではありません。」

“Dispose, Finalization, and Resource Management Design Guidelines”に多くのより多くの情報があります。

編集:リンクインサイド

+1

私は他の人:なぜファイナライザからそれを呼び出すのですか? – ryeguy

+0

@ryeguy実際に実際に行う必要がなければ、実際にファイナライザを実装してはならないからです。 –

+0

あなたの「もっと多くの情報」リンクは素晴らしいです! –

1

(廃棄)あなたはファイナライザは、これらのOBと呼ばれる.Whenアンマネージリソース(例えばデータベース接続を)持っている管理対象オブジェクトに近い/処分を呼び出すことになっている場合オブジェクトには到達できないため、オブジェクト自体をファイナライズすることができ、そのオブジェクトを処分する必要はありません。ファイナライズの順序も決まっていないので、すでに配置されているオブジェクトに対してdisposeを呼び出すことがあります。

3

私は混乱があなたの例では、任意のアンマネージリソースを解放していないことに起因していると思います。これらはまた、ガベージコレクションを介してdisposeが呼び出されたときに解放される必要があります。の外側にある場合、disposingのチェックがリリースされます。 releasing unmanaged resourcesに関するMSDNの例を参照してください。チェックの外側に起こる/そうでなければならないもう一つは、任意の基本クラス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); 
    } 
+0

'〜Derived()についてはどうですか? { Dispose(false); } 'の派生ですか? –

関連する問題