Dispose メソッドの実装

任意の型の Dispose メソッドは、所有するすべてのリソースを解放する必要があります。また、その型の基本型が所有しているすべてのリソースも、親の型の Dispose メソッドを呼び出して解放する必要があります。親の型の Dispose メソッドは、所有するすべてのリソースを解放した後、それ自身の親の型の Dispose メソッドを呼び出す必要があります。この処理が、基本型の階層全体を通じて繰り返されます。Dispose メソッドが複数回呼び出される場合でも、例外をスローすることなく呼び出されるようにして、リソースが常に適切にクリーンアップされるようにする必要があります。

重要

C++ プログラマはこのトピックを使用しないでください。代わりに、Destructors and Finalizers in Visual C++ を参照してください。.NET Framework Version 2.0 では、C++ コンパイラはリソースの確定的な破棄の実装をサポートし、Dispose メソッドを直接実装することは許可しません。

Dispose メソッドは、破棄するオブジェクトの GC.SuppressFinalize メソッドを呼び出す必要があります。GC.SuppressFinalize を呼び出すと、オブジェクトが終了キューに置かれている場合は、そのオブジェクトの Finalize メソッドの呼び出しは行われません。Finalize メソッドの実行は、パフォーマンスに影響を与えることを覚えておいてください。Dispose メソッドがオブジェクトのクリーンアップを完了していれば、ガベージ コレクタがそのオブジェクトの Finalize メソッドを呼び出す必要はなくなります。

Noteメモ :

System.GC.KeepAlive(System.Object) メソッドのコード例では、再利用するオブジェクトのメンバがまだ実行中の場合でも、ガベージ コレクションがファイナライザを実行しようとします。長い Dispose メソッドの最後に、KeepAlive メソッドを呼び出すことをお勧めします。

次のコード例は、Dispose メソッドをアンマネージ リソースをカプセル化したクラスに実装する推奨デザイン パターンを示すことを目的としています。このパターンは、.NET Framework 全体で実装されます。

リソース クラスは、通常はネイティブなクラスや API の組み合わせから派生されるため、その組み合わせに応じてカスタマイズする必要があります。このコード パターンを元にしてリソース クラスを作成し、カプセル化するリソースに応じて必要なカスタマイズを行います。このサンプルは、コンパイルしたり、アプリケーションに直接組み込んで使用したりすることはできません。

この例では、基本クラスの BaseResource は、クラスのユーザーが呼び出すことのできるパブリックな Dispose メソッドを実装します。このメソッドは、virtual Dispose(bool disposing) メソッド (Visual Basic では virtual Dispose(disposing As Boolean) メソッド) を呼び出します。呼び出し元の ID に応じて、true または false が渡されます。オブジェクトに対する適切なクリーンアップ コードは、仮想 Dispose メソッドで実行されます。

Dispose(bool disposing) メソッドは、異なる 2 種類のシナリオで実行されます。disposing が true の場合、このメソッドはユーザーのコードによって直接または間接に呼び出され、マネージ リソースとアンマネージ リソースの両方を破棄できます。disposing が false の場合、このメソッドは共通言語ランタイムによってファイナライザの内部から呼び出され、アンマネージ リソースだけを破棄できます。ファイナライザは特定の順序で実行されないため、オブジェクトがその終了コードを実行しているときには、他のオブジェクトを参照しないようにする必要があります。実行中のファイナライザが、既に終了されている別のオブジェクトを参照した場合、そのファイナライザは失敗します。

基本クラスでは、Dispose が呼び出されなかった場合の備えとして、Finalize メソッドまたはデストラクタが提供されています。Finalize メソッドは、複数のパラメータを受け取る Dispose メソッドを呼び出し、false を渡します。Finalize メソッド内で Dispose クリーンアップ コードを再作成しないでください。読みやすく保守のしやすいコードを作成するには、Dispose(false) の呼び出しが最も適しています。

MyResourceWrapper クラスでは、Dispose を使用したリソース管理を実装したクラスから派生する方法を示します。MyResourceWrappervirtual Dispose(bool disposing) メソッドをオーバーライドし、このクラス自体が作成したマネージ リソースおよびアンマネージ リソースのクリーンアップ コードを提供します。また、MyResourceWrapper はその基本クラスである BaseResourceDispose も呼び出し、基本クラスでも正しくクリーンアップを実行できるようにします。派生された MyResourceWrapper クラスは、Finalize メソッドや Dispose メソッドを基本クラスである BaseResource から継承するため、パラメータのない Finalize メソッドや Dispose メソッドを持ちません。

Noteメモ :

この例の protected Dispose(bool disposing) メソッドはユーザー スレッドとファイナライザ スレッドから同時に呼び出すことができないため、スレッド セーフは実現されていません。また、BaseResource を使用するクライアント アプリケーションでは、複数のユーザー スレッドが同時に protected Dispose(bool disposing) メソッドを呼び出すことを許可しないでください。アプリケーションやクラス ライブラリは、1 つのスレッドだけがリソースの有効期間を所有し、リソースが不要になったときに Dispose を呼び出すことを許可するようにデザインする必要があります。リソースによっては、リソースを破棄するときの非同期スレッド アクセスにより、セキュリティ上のリスクが伴う場合があります。開発者はコードを入念に調べて、スレッド セーフを実現するための最善の方法を決定する必要があります。

' Design pattern for the base class.
' By implementing IDisposable, you are announcing that instances
' of this type allocate scarce resources.
Public Class BaseResource
   Implements IDisposable
   ' Pointer to an external unmanaged resource.
   Private handle As IntPtr 
   ' Other managed resource this class uses.
   Private Components As Component
   ' Track whether Dispose has been called.
   Private disposed As Boolean = False

   ' Constructor for the BaseResource Object.
   Public Sub New()
      ' Insert appropriate constructor code here.
   End Sub

   ' Implement IDisposable.
   ' Do not make this method Overridable.
   ' A derived class should not be able to override this method.
   Public Overloads Sub Dispose()Implements IDisposable.Dispose
      Dispose(true)
      ' Take yourself off of the finalization queue
      ' to prevent finalization code for this object
      ' from executing a second time.
      GC.SuppressFinalize(Me) 
   End Sub

' Dispose(disposing As Boolean) executes in two distinct scenarios.
' If disposing is 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.
Protected Overloads Overridable Sub Dispose(disposing As Boolean)
   ' Check to see if Dispose has already been called.
   If Not (Me.disposed) Then
      ' If disposing equals true, dispose all managed 
      ' and unmanaged resources.
      If (disposing) Then
         ' Dispose managed resources.
         Components.Dispose()
      End If
      ' Release unmanaged resources. If disposing is false,
      ' only the following code is executed.      
      CloseHandle(handle)
      handle = IntPtr.Zero
      ' Note that this is not thread safe.
      ' Another thread could start disposing the object
      ' after the managed resources are disposed,
      ' but before the disposed flag is set to true.
      ' If thread safety is necessary, it must be
      ' implemented by the client.
   End If
   Me.disposed = true
End Sub

   ' This Finalize method will run only if the 
   ' Dispose method does not get called.
   ' By default, methods are NotOverridable. 
   ' This prevents a derived class from overriding this method.
   Protected Overrides Sub Finalize()
         ' Do not re-create Dispose clean-up code here.
         ' Calling Dispose(false) is optimal in terms of
         ' readability and maintainability.
         Dispose(false)
   End Sub
   
   ' Allow your Dispose method to be called multiple times,
   ' but throw an exception if the object has been disposed.
   ' Whenever you do something with this class, 
   ' check to see if it has been disposed.
   Public Sub DoSomething()
      If Me.disposed Then
         Throw New ObjectDisposedException()
      End if
   End Sub
End Class

' Design pattern for a derived class.
' Note that this derived class inherently implements the 
' IDisposable interface because it is implemented in the base class.
Public Class MyResourceWrapper
   Inherits BaseResource
   
   ' A managed resource that you add in this derived class.
   private addedManaged As ManagedResource
   ' A native unmanaged resource that you add in this derived class.
   private addedNative As NativeResource
   ' Track whether Dispose has been called.
   Private disposed As Boolean = False

   ' Constructor for the MyResourceWrapper object.
   Public Sub New()      
      MyBase.New()
      ' Insert appropriate constructor code here for the
      ' added resources.
   End Sub

   Protected Overloads Overrides Sub Dispose(disposing As Boolean)
      If Not (Me.disposed) Then
         Try
            If disposing Then
              ' Release the managed resources you added in
              ' this derived class here.
              addedManaged.Dispose()
            End If
            ' Release the native unmanaged resources you added
            ' in this derived class here.
            CloseHandle(addedNative)
            Me.disposed = true
         Finally
            ' Call Dispose on your base class.
            MyBase.Dispose(disposing)
         End Try
      End If
   End Sub
End Class
' This derived class does not have a Finalize method
' or a Dispose method without parameters because it 
' inherits them from the base class.
// Design pattern for the base class.
// By implementing IDisposable, you are announcing that instances
// of this type allocate scarce resources.
public class BaseResource: IDisposable
{
   // Pointer to an external unmanaged resource.
   private IntPtr handle;
   // Other managed resource this class uses.
   private Component Components;
   // Track whether Dispose has been called.
   private bool disposed = false;

   // Constructor for the BaseResource object.
   public BaseResource()
   {
      // Insert appropriate constructor code here.
   }

   // Implement IDisposable.
   // Do not make this method virtual.
   // A derived class should not be able to override this method.
   public void Dispose()
   {
      Dispose(true);
      // Take yourself off the Finalization queue 
      // to 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.
   protected virtual 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.
            Components.Dispose();
         }
         // Release unmanaged resources. If disposing is false, 
         // only the following code is executed.
         CloseHandle(handle);
         handle = IntPtr.Zero;
         // Note that this is not thread safe.
         // Another thread could start disposing the object
         // after the managed resources are disposed,
         // but before the disposed flag is set to true.
         // If thread safety is necessary, it must be
         // implemented by the client.

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

   // Allow your Dispose method to be called multiple times,
   // but throw an exception if the object has been disposed.
   // Whenever you do something with this class, 
   // check to see if it has been disposed.
   public void DoSomething()
   {
      if(this.disposed)
      {
         throw new ObjectDisposedException();
      }
   }
}

// Design pattern for a derived class.
// Note that this derived class inherently implements the 
// IDisposable interface because it is implemented in the base class.
public class MyResourceWrapper: BaseResource
{
   // A managed resource that you add in this derived class.
   private ManagedResource addedManaged;
   // A native unmanaged resource that you add in this derived class.
   private NativeResource addedNative;
   private bool disposed = false;

  // Constructor for this object.
   public MyResourceWrapper()
   {
      // Insert appropriate constructor code here.
   }

   protected override void Dispose(bool disposing)
   {
      if(!this.disposed)
      {
         try
         {
            if(disposing)
            {
               // Release the managed resources you added in
               // this derived class here.
               addedManaged.Dispose();         
            }
            // Release the native unmanaged resources you added
            // in this derived class here.
            CloseHandle(addedNative);
            this.disposed = true;
         }
         finally
         {
            // Call Dispose on your base class.
            base.Dispose(disposing);
         }
      }
   }
}

// This derived class does not have a Finalize method
// or a Dispose method without parameters because it inherits 
// them from the base class.

Close メソッドの実装

Dispose メソッドを呼び出すよりも Close メソッドを呼び出す方が適切な型の場合は、その基本型にパブリックな Close メソッドを追加します。この Close メソッドは、パラメータを指定せずに Dispose メソッドを呼び出します。この Dispose メソッドは適切なクリーンアップ操作を実行します。Close メソッドのコード例を次に示します。

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

参照

関連項目

GC.SuppressFinalize Method
Destructors and Finalizers in Visual C++
アンマネージ リソースをクリーンアップするための Finalize および Dispose の実装

概念

Finalize メソッドのオーバーライド