Dispose メソッドの実装

 

アプリケーションによって使用されるアンマネージ リソースを解放するための Dispose メソッドを実装します。 .NET Framework ガベージ コレクターは、アンマネージ メモリの割り当てや解放を行いません。

呼ばれる、オブジェクトを破棄するパターン、 dispose パターンオブジェクトの有効期間に順番が付けられます。 Dispose パターンは、ファイルおよびパイプ ハンドル、レジストリ ハンドル、待機ハンドル、アンマネージ メモリ ブロックのポインターなど、アンマネージ リソースにアクセスするオブジェクトでのみ使用されます。 これは、使用されていないマネージ オブジェクトの解放にはガベージ コレクターが非常に有効ですが、アンマネージ オブジェクトは解放できないためです。

Dispose パターンには&2; 種類あります。

  • 型で使用する各アンマネージ リソースをセーフ ハンドル (つまり、System.Runtime.InteropServices.SafeHandle から派生したクラス) でラップします。 この場合、IDisposable インターフェイスと追加の Dispose(Boolean) メソッドを実装します。 これは推奨される方法で、Object.Finalize メソッドをオーバーライドする必要がありません。

    System_CAPS_ICON_note.jpg メモ

    Microsoft.Win32.SafeHandles 名前空間には SafeHandle から派生した一連のクラスが用意されています。これらのクラスは「セーフ ハンドルの使用」にリストされています。 アンマネージ リソースを解放できるクラスが見つからない場合は、SafeHandle の独自のサブクラスを実装できます。

  • 実装する、 IDisposableインターフェイスと追加Dispose(Boolean)メソッドもオーバーライド、 Object.Finalizeメソッドです。 IDisposable.Dispose の実装が型のコンシューマーによって呼び出されなかった場合にアンマネージ リソースが破棄されるように、Finalize をオーバーライドする必要があります。 前の項目で説明された推奨される方法を使用すると、System.Runtime.InteropServices.SafeHandle クラスが代わりにこれを実行します。

Dispose メソッドが複数回呼び出される場合でも、例外をスローすることなく呼び出されるようにして、リソースが常に適切にクリーンアップされるようにする必要があります。

System_CAPS_ICON_important.jpg 重要

C++ プログラマならを実装していない、 Disposeメソッドです。 「デストラクターとファイナライザー」セクションの指示に従って、代わりに、する方法: 定義消費クラスと構造体 (C + +/CLI)します。 以降、.NET Framework 2.0 では、C++ コンパイラはリソースの確定的な破棄をサポートしの直接の実装を許可しない、 Disposeメソッドです。

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

IDisposable インターフェイスでは、パラメーターのない Dispose メソッドを&1; つ実装する必要があります。 しかし、Dispose パターンでは&2; 種類の Dispose メソッドを実装する必要があります。

  • public で非仮想 (Visual Basic では NonInheritable) の IDisposable.Dispose の実装。パラメーターはありません。

  • protected で仮想 (Visual Basic では Overridable) の Dispose メソッド。シグネチャは次のとおりです。

       protected virtual void Dispose(bool disposing)
    

Dispose() オーバーロード

この public で非仮想 (Visual Basic では NonInheritable)、パラメーターなしの Dispose メソッドは、型のコンシューマーによって呼び出されるため、その目的は、アンマネージ リソースを解放し、ファイナライザーが存在する場合、それを実行する必要がないことを示すことです。 このため、次のような標準的な実装があります。

   public void Dispose()
   {
      // Dispose of unmanaged resources.
      Dispose(true);
      // Suppress finalization.
      GC.SuppressFinalize(this);
   }

Dispose メソッドはオブジェクトのクリーンアップをすべて実行するため、ガベージ コレクターはもはやオブジェクトの Object.Finalize のオーバーライドを呼び出す必要はありません。 そのため、呼び出しをSuppressFinalizeメソッドにより、ガベージ コレクターによるファイナライザーの実行します。 型にファイナライザーの呼び出しにないかどうかはGC。SuppressFinalizeも何も起こりません。 アンマネージ リソースを解放する実際の作業は Dispose メソッドの&2; 番目のオーバーロードによって実行されることに注意してください。

Dispose(Boolean) オーバーロード

2 番目のオーバー ロードでは、disposingパラメーターは、ブールメソッドの呼び出し元となるかどうかを示す、 Disposeメソッド (その値はtrue) それともファイナライザーか (その値はfalse)。

メソッドの本体は&2; つのコード ブロックで構成されます。

  • アンマネージ リソースを解放するブロック。 このブロックは、disposing パラメーターの値に関係なく実行されます。

  • マネージ リソースを解放する条件付きブロック。 このブロックは、disposing の値が true の場合に実行されます。 解放するマネージ リソースには、次のオブジェクトを含めることができます。

    実装するマネージ オブジェクトIDisposableします。
    条件付きブロックを使用して Dispose の実装を呼び出すことができます。 呼び出す必要がありますをアンマネージ リソースをラップするセーフ ハンドルを使用した場合、 SafeHandle.Dispose(Boolean)ここでの実装です。

    大量のメモリを消費するか、不足しているリソースを消費するマネージ オブジェクト。
    これらのオブジェクトを Dispose メソッドで明示的に解放すると、ガベージ コレクターによって非確定的に解放される場合よりも迅速に解放されます。

メソッドの呼び出し元がファイナライザーの場合 (つまり、disposingfalse の場合)、アンマネージ リソースを解放するコードだけが実行されます。 ガベージ コレクターが終了処理の際にマネージ オブジェクトを破棄する順序は定義されていないため、値 Dispose を指定した false オーバーロードを呼び出すことで、既に解放されている可能性のあるマネージ リソースをファイナライザーが解放しようとすることを防止できます。

基底クラスで Dispose パターンを実装する場合、以下の項目を用意する必要があります。

System_CAPS_ICON_important.jpg 重要

すべての基底クラスを実装するには、このパターンを実装する必要がありますIDisposableされないsealed(NotInheritable Visual Basic で)。

  • Dispose(Boolean) メソッドを呼び出す Dispose の実装。

  • リソースを解放する実際の作業を実行する Dispose(Boolean) メソッド。

  • アンマネージ リソースをラップする SafeHandle から派生したクラス (推奨)、または、Object.Finalize メソッドのオーバーライド。 SafeHandle クラスには、コーディングが不要なファイナライザーが用意されています。

セーフ ハンドルを使用して基底クラスで Dispose パターンを実装する一般的なパターンを次に示します。

using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;

class BaseClass : IDisposable
{
   // Flag: Has Dispose already been called?
   bool disposed = false;
   // Instantiate a SafeHandle instance.
   SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);
   
   // Public implementation of Dispose pattern callable by consumers.
   public void Dispose()
   { 
      Dispose(true);
      GC.SuppressFinalize(this);           
   }
   
   // Protected implementation of Dispose pattern.
   protected virtual void Dispose(bool disposing)
   {
      if (disposed)
         return; 
      
      if (disposing) {
         handle.Dispose();
         // Free any other managed objects here.
         //
      }
      
      // Free any unmanaged objects here.
      //
      disposed = true;
   }
}

System_CAPS_ICON_note.jpg メモ

前の例では、SafeFileHandle オブジェクトを使用してパターンを示しています。代わりに、SafeHandle から派生した任意のオブジェクトを使用することもできます。 例では、SafeFileHandle オブジェクトを正しくインスタンス化していないことに注意してください。

Object.Finalize をオーバーライドして基底クラスで Dispose パターンを実装する一般的なパターンを次に示します。

using System;

class BaseClass : IDisposable
{
   // Flag: Has Dispose already been called?
   bool disposed = false;
   
   // Public implementation of Dispose pattern callable by consumers.
   public void Dispose()
   { 
      Dispose(true);
      GC.SuppressFinalize(this);           
   }
   
   // Protected implementation of Dispose pattern.
   protected virtual void Dispose(bool disposing)
   {
      if (disposed)
         return; 
      
      if (disposing) {
         // Free any other managed objects here.
         //
      }
      
      // Free any unmanaged objects here.
      //
      disposed = true;
   }

   ~BaseClass()
   {
      Dispose(false);
   }
}

System_CAPS_ICON_note.jpg メモ

オーバーライドする、C# の場合は、 Object.Finalize定義することで、デストラクターします。

IDisposable インターフェイスを実装するクラスから派生したクラスは、IDisposable.Dispose の基底クラスでの実装が派生クラスに継承されるため、IDisposable を実装しないでください。 代わりに、派生クラスで Dispose パターンを実装するには、以下の項目を用意します。

  • 基底クラスのメソッドをオーバーライドして、派生クラスのリソースを解放する実際の作業を実行する protected``Dispose(Boolean) メソッド。 このメソッドは、基底クラスの Dispose(Boolean) メソッドも呼び出して、それに true 引数の値として disposing を渡す必要があります。

  • アンマネージ リソースをラップする SafeHandle から派生したクラス (推奨)、または、Object.Finalize メソッドのオーバーライド。 SafeHandle クラスには、コーディングが不要なファイナライザーが用意されています。 ファイナライザーを用意する場合は、Dispose(Boolean) 引数を disposing として false オーバーロードを呼び出す必要があります。

セーフ ハンドルを使用して派生クラスで Dispose パターンを実装する一般的なパターンを次に示します。

using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;

class DerivedClass : BaseClass
{
   // Flag: Has Dispose already been called?
   bool disposed = false;
   // Instantiate a SafeHandle instance.
   SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);

   // Protected implementation of Dispose pattern.
   protected override void Dispose(bool disposing)
   {
      if (disposed)
         return; 
      
      if (disposing) {
         handle.Dispose();
         // Free any other managed objects here.
         //
      }
      
      // Free any unmanaged objects here.
      //

      disposed = true;
      // Call base class implementation.
      base.Dispose(disposing);
   }
}

System_CAPS_ICON_note.jpg メモ

前の例では、SafeFileHandle オブジェクトを使用してパターンを示しています。代わりに、SafeHandle から派生した任意のオブジェクトを使用することもできます。 例では、SafeFileHandle オブジェクトを正しくインスタンス化していないことに注意してください。

Object.Finalize をオーバーライドして派生クラスで Dispose パターンを実装する一般的なパターンを次に示します。

using System;

class DerivedClass : BaseClass
{
   // Flag: Has Dispose already been called?
   bool disposed = false;
   
   // Protected implementation of Dispose pattern.
   protected override void Dispose(bool disposing)
   {
      if (disposed)
         return; 
      
      if (disposing) {
         // Free any other managed objects here.
         //
      }
      
      // Free any unmanaged objects here.
      //
      disposed = true;
      
      // Call the base class implementation.
      base.Dispose(disposing);
   }

   ~DerivedClass()
   {
      Dispose(false);
   }
}

System_CAPS_ICON_note.jpg メモ

オーバーライドする、C# の場合は、 Object.Finalize定義することで、デストラクターします。

オブジェクトのファイナライザーのコードを記述することは、正しく行わないと問題が発生する可能性がある複雑なタスクです。 そのため、ファイナライザーを実装するのではなく、System.Runtime.InteropServices.SafeHandle オブジェクトを構築することをお勧めします。

System.Runtime.InteropServices.SafeHandle クラスから派生したクラスは、処理を中断することなくハンドルの割り当てと解放を行うことで、オブジェクトの有効期間に関する問題を単純化します。 セーフ ハンドルは、アプリケーション ドメインのアンロード中に確実に実行されるクリティカル ファイナライザーを含んでいます。 セーフ ハンドルを使用する利点の詳細については、次を参照してください。 System.Runtime.InteropServices.SafeHandleします。 Microsoft.Win32.SafeHandles 名前空間の次の派生クラスは、セーフ ハンドルを提供します。

次の例は、セーフ ハンドルを使用してアンマネージ リソースをカプセル化する、基底クラス DisposableStreamResource での Dispose パターンを示します。 例では、SafeFileHandle を使用して、開いているファイルを表す Stream オブジェクトをラップする DisposableResource クラスを定義しています。 DisposableResource メソッドには、ファイル ストリームの合計バイト数を返す Size プロパティも含まれています。

using Microsoft.Win32.SafeHandles;
using System;
using System.IO;
using System.Runtime.InteropServices;

public class DisposableStreamResource : IDisposable
{
   // Define constants.
   protected const uint GENERIC_READ = 0x80000000;
   protected const uint FILE_SHARE_READ = 0x00000001;
   protected const uint OPEN_EXISTING = 3;
   protected const uint FILE_ATTRIBUTE_NORMAL = 0x80;
   protected IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
   private const int INVALID_FILE_SIZE = unchecked((int) 0xFFFFFFFF);
   
   // Define Windows APIs.
   [DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode)]
   protected static extern IntPtr CreateFile (
                                  string lpFileName, uint dwDesiredAccess, 
                                  uint dwShareMode, IntPtr lpSecurityAttributes, 
                                  uint dwCreationDisposition, uint dwFlagsAndAttributes, 
                                  IntPtr hTemplateFile);
   
   [DllImport("kernel32.dll")]
   private static extern int GetFileSize(SafeFileHandle hFile, out int lpFileSizeHigh);
    
   // Define locals.
   private bool disposed = false;
   private SafeFileHandle safeHandle; 
   private long bufferSize;
   private int upperWord;
   
   public DisposableStreamResource(string filename)
   {
      if (filename == null)
         throw new ArgumentNullException("The filename cannot be null.");
      else if (filename == "")
         throw new ArgumentException("The filename cannot be an empty string.");
            
      IntPtr handle = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ,
                                 IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
                                 IntPtr.Zero);
      if (handle != INVALID_HANDLE_VALUE)
         safeHandle = new SafeFileHandle(handle, true);
      else
         throw new FileNotFoundException(String.Format("Cannot open '{0}'", filename));
      
      // Get file size.
      bufferSize = GetFileSize(safeHandle, out upperWord); 
      if (bufferSize == INVALID_FILE_SIZE)
         bufferSize = -1;
      else if (upperWord > 0) 
         bufferSize = (((long)upperWord) << 32) + bufferSize;
   }
   
   public long Size 
   { get { return bufferSize; } }

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

   protected virtual void Dispose(bool disposing)
   {
      if (disposed) return;

      // Dispose of managed resources here.
      if (disposing)
         safeHandle.Dispose();
      
      // Dispose of any unmanaged resources not wrapped in safe handles.
      
      disposed = true;
   }  
}

次の例は、前の例で挙げた DisposableStreamResource2 クラスを継承した派生クラス DisposableStreamResource での Dispose パターンを示します。 このクラスは WriteFileInfo メソッドを追加し、SafeFileHandle オブジェクトを使用して書き込み可能ファイル ハンドルをラップしています。

using Microsoft.Win32.SafeHandles;
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;

public class DisposableStreamResource2 : DisposableStreamResource
{
   // Define additional constants.
   protected const uint GENERIC_WRITE = 0x40000000; 
   protected const uint OPEN_ALWAYS = 4;
   
   // Define additional APIs.
   [DllImport("kernel32.dll")]   
   protected static extern bool WriteFile(
                                SafeFileHandle safeHandle, string lpBuffer, 
                                int nNumberOfBytesToWrite, out int lpNumberOfBytesWritten,
                                IntPtr lpOverlapped);
   
   // Define locals.
   private bool disposed = false;
   private string filename;
   private bool created = false;
   private SafeFileHandle safeHandle;
   
   public DisposableStreamResource2(string filename) : base(filename)
   {
      this.filename = filename;
   }
   
   public void WriteFileInfo()
   { 
      if (! created) {
         IntPtr hFile = CreateFile(@".\FileInfo.txt", GENERIC_WRITE, 0, 
                                   IntPtr.Zero, OPEN_ALWAYS, 
                                   FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
         if (hFile != INVALID_HANDLE_VALUE)
            safeHandle = new SafeFileHandle(hFile, true);
         else
            throw new IOException("Unable to create output file.");

         created = true;
      }

      string output = String.Format("{0}: {1:N0} bytes\n", filename, Size);
      int bytesWritten;
      bool result = WriteFile(safeHandle, output, output.Length, out bytesWritten, IntPtr.Zero);                                     
   }

   protected new virtual void Dispose(bool disposing)
   {
      if (disposed) return;
      
      // Release any managed resources here.
      if (disposing)
         safeHandle.Dispose();
      
      disposed = true;
      
      // Release any unmanaged resources not wrapped by safe handles here.
      
      // Call the base class implementation.
      base.Dispose(true);
   }
}

SuppressFinalize
IDisposable
IDisposable.Dispose
Microsoft.Win32.SafeHandles
System.Runtime.InteropServices.SafeHandle
Object.Finalize
方法: クラスと構造体定義および使用 (C + +/CLI)
Dispose パターン

表示: