アンマネージ リソースをクリーンアップするための Finalize および Dispose の実装

更新 : 2007 年 11 月

メモ :

C++ を使用したファイナライズと破棄については、「Destructors and Finalizers in Visual C++」を参照してください。

クラスのインスタンスは、ウィンドウ ハンドル (HWND)、データベース接続など、ランタイムで管理されないリソースに対する制御をカプセル化することがよくあります。そのため、これらのリソースを解放する明示的な方法と暗黙的な方法の両方をサポートする必要があります。暗黙的な制御を提供するには、オブジェクトにプロテクト Finalize を実装します (C# および C++ のデストラクタ構文)。そのオブジェクトへの有効な参照がなくなった後で、ガベージ コレクタがこのメソッドを呼び出します。

場合によっては、ガベージ コレクタがオブジェクトを解放する前に、そのオブジェクトを使用するプログラマが明示的にこれらの外部リソースを解放できるようにする必要があります。外部リソースが不足している場合、またはパフォーマンス低下の原因となっている場合には、それらのリソースが使用されなくなったときにプログラマが明示的に解放することによって、パフォーマンスの向上を図ることができます。明示的な制御を提供するには、IDisposable で用意されている Dispose を実装します。オブジェクトのコンシューマは、オブジェクトの使用を終了したら、Dispose メソッドを呼び出す必要があります。Dispose は、オブジェクトへの他の参照が有効なときでも呼び出すことができます。

Dispose によって明示的な制御を提供した場合でも、Finalize メソッドを使用する暗黙的なクリーンアップを提供する必要があります。Finalize は、プログラマが Dispose を呼び出さなかった場合に、リソースが永久にリークされることを防ぐためのバックアップとして提供します。

アンマネージ リソースをクリーンアップするための Finalize および Dispose の実装の詳細については、「ガベージ コレクション」を参照してください。Dispose を実装するための基本デザイン パターンを次のコード例に示します。この例では System 名前空間が必要です。

' Design pattern for a base class.

Public Class Base
   Implements IDisposable
    ' Field to handle multiple calls to Dispose gracefully.
    Dim disposed as Boolean = false

   ' Implement IDisposable.
   Public Overloads Sub Dispose() Implements IDisposable.Dispose
      Dispose(True)
      GC.SuppressFinalize(Me)
   End Sub

   Protected Overloads Overridable Sub Dispose(disposing As Boolean)
      If disposed = False Then
          If disposing Then
             ' Free other state (managed objects).
             disposed = True
          End If
      End If
      ' Free your own state (unmanaged objects).
      ' Set large fields to null.
   End Sub

   Protected Overrides Sub Finalize()
      ' Simply call Dispose(False).
      Dispose (False)
   End Sub
End Class

' Design pattern for a derived class.
Public Class Derived
   Inherits Base

    ' Field to handle multiple calls to Dispose gracefully.
    Dim disposed as Boolean = false

   Protected Overloads Overrides Sub Dispose(disposing As Boolean) 
      If disposed = False Then
          If disposing Then 
             ' Release managed resources.
             disposed = True
          End If
      End If
      ' Release unmanaged resources.
      ' Set large fields to null.
      ' Call Dispose on your base class.
      Mybase.Dispose(disposing)
   End Sub
   ' The derived class does not have a Finalize method
   ' or a Dispose method without parameters because it inherits
   ' them from the base class.
End Class
// Design pattern for a base class.
public class Base: IDisposable
{
   //Implement IDisposable.
   public void Dispose() 
   {
     Dispose(true);
      GC.SuppressFinalize(this); 
   }

   protected virtual void Dispose(bool disposing) 
   {
      if (disposing) 
      {
         // Free other state (managed objects).
      }
      // Free your own state (unmanaged objects).
      // Set large fields to null.
   }

   // Use C# destructor syntax for finalization code.
   ~Base()
   {
      // Simply call Dispose(false).
      Dispose (false);
   }
}
// Design pattern for a derived class.
public class Derived: Base
{   
   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);
   }
   // The derived class does not have a Finalize method
   // or a Dispose method without parameters because it inherits
   // them from the base class.
}

Finalize および Dispose を実装するデザイン パターンのさらに詳細なコード例については、「Dispose メソッドの実装」を参照してください。

Dispose メソッド名のカスタマイズ

場合によっては、Dispose ではなく、ドメイン固有の名前を付ける方が適切なこともあります。たとえば、ファイルのカプセル化では、メソッド名として Close を使用した方が適切です。この場合は、Dispose をプライベートに実装し、Dispose を呼び出すパブリックな Close メソッドを作成します。このパターンを説明するコード例を次に示します。Close は、ドメインに適したメソッド名に置き換えることができます。この例では System 名前空間が必要です。

' Do not make this method overridable.
' A derived class should not be allowed
' to override this method.
Public Sub Close()
   ' Call the Dispose method with no parameters.
   Dispose()
End Sub
// Do not make this method virtual.
// A derived class should not be allowed
// to override this method.
public void Close()
{
   // Call the Dispose method with no parameters.
   Dispose();
}

Finalize

次の規則は、Finalize メソッドの使用方法のガイドラインを示しています。

  • 終了処理が必要なオブジェクトでだけ Finalize を実装します。Finalize メソッドを実装すると、パフォーマンスが低下します。

  • Finalize メソッドが必要な場合は、クラスを使用するときに Finalize メソッドを呼び出すことによるパフォーマンスの低下を回避できるように、IDisposable を実装することを検討する必要があります。

  • Finalize メソッドの参照可能性は限定します。つまり、public ではなく、protected にします。

  • オブジェクトの Finalize メソッドは、そのオブジェクトが保持しているすべての外部リソースを解放する必要があります。さらに、Finalize メソッドは、そのオブジェクトが保持しているリソースだけを解放する必要があります。Finalize メソッドは、他のオブジェクトは参照できません。

  • オブジェクトの基本クラス以外のオブジェクトに対しては Finalize メソッドを直接呼び出さないようにします。これは、C# プログラミング言語では正しい操作ではありません。

  • オブジェクトの Finalize メソッドから基本クラスの Finalize メソッドを呼び出します。

    メモ :

    基本クラスの Finalize メソッドは、C# および C++ のデストラクタ構文によって自動的に呼び出されます。

Dispose

次の規則は、Dispose メソッドの使用方法のガイドラインを示しています。

  • Dispose のデザイン パターンは、明示的に解放する必要があるリソースをカプセル化する型に対して実装します。パブリックな Dispose メソッドを呼び出すことによって、外部リソースを解放できるようにします。

  • Dispose のデザイン パターンは、基本型そのものがリソースを保持するかどうかにかかわらず、一般にリソースを保持する派生型を持つ基本型に対して実装します。基本型に Close メソッドがある場合は、多くの場合、Dispose を実装する必要があります。この場合は、その基本型に対して Finalize メソッドを実装しないでください。Finalize は、クリーンアップが必要なリソースを保持する派生型で実装する必要があります。

  • Dispose メソッドは、その型が所有している破棄可能なすべてのリソースを解放します。

  • インスタンスで Dispose が呼び出された後、GC.SuppressFinalize を呼び出すことにより、Finalize メソッドが実行されることがないようにします。まれに、この規則の例外として、Dispose で処理されない作業を Finalize で実行することもあります。

  • IDisposable を実装する場合は、基本クラスの Dispose メソッドを呼び出します。

  • Dispose が呼び出されることを前提にはしないでください。Dispose が呼び出されなかった場合は、型が所有するアンマネージ リソースも Finalize メソッドで解放する必要があります。

  • リソースが既に破棄されている場合は、この型 (Dispose 以外) のインスタンス メソッドから ObjectDisposedException をスローします。Dispose メソッドは、例外をスローせずに複数回呼び出すことができなければならないため、Dispose メソッドにはこの規則は適用されません。

  • 基本型の階層構造を通じて、Dispose 呼び出しを反映させます。Dispose メソッドは、対象のオブジェクトおよびそのオブジェクトが所有するすべてのオブジェクトが保持する全リソースを解放する必要があります。たとえば、Stream と Encoding を保持する TextReader のようなオブジェクトを作成できますが、この Stream と Encoding は、いずれも TextReader によって開発者の知らない間に作成されます。さらに、Stream と Encoding は、いずれも外部リソースを取得します。TextReader に対して Dispose メソッドを呼び出す場合は、それらの外部リソースも解放できるように、Stream と Encoding に対しても Dispose が呼び出されるようにしておく必要があります。

  • オブジェクトに対して Dispose メソッドを呼び出した後は、そのオブジェクトを使用できないようにすることを検討してください。既に破棄されたオブジェクトの再作成は、実装するのが困難なパターンです。

  • 例外がスローされることなく、Dispose メソッドを複数回呼び出すことができるようにします。最初の呼び出し以外では、何もしないようにします。

Portions Copyright 2005 Microsoft Corporation.All rights reserved.

Portions Copyright Addison-Wesley Corporation.All rights reserved.

デザイン ガイドラインの詳細については、2005 年に Addison-Wesley から出版されている Krzysztof Cwalina、Brad Abrams 共著の『Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries』を参照してください。

参照

参照

IDisposable.Dispose

Object.Finalize

その他の技術情報

クラス ライブラリ開発のデザイン ガイドライン

ガベージ コレクション

デザイン パターン