Dispose メソッドの実装Implementing a Dispose method

アプリケーションによって使用されるアンマネージ リソースを解放するための Dispose メソッドを実装します。You implement a Dispose method to release unmanaged resources used by your application. .NET のガベージ コレクターは、アンマネージ メモリの割り当てや解放を行いません。The .NET garbage collector does not allocate or release unmanaged memory.

Dispose パターンと呼ばれる、オブジェクトを破棄するパターンによって、オブジェクトの有効期間に順番が付けられます。The pattern for disposing an object, referred to as a dispose pattern, imposes order on the lifetime of an object. Dispose パターンは、ファイルおよびパイプ ハンドル、レジストリ ハンドル、待機ハンドル、アンマネージ メモリ ブロックのポインターなど、アンマネージ リソースにアクセスするオブジェクトでのみ使用されます。The dispose pattern is used only for objects that access unmanaged resources, such as file and pipe handles, registry handles, wait handles, or pointers to blocks of unmanaged memory. これは、使用されていないマネージ オブジェクトの解放にはガベージ コレクターが非常に有効ですが、アンマネージ オブジェクトは解放できないためです。This is because the garbage collector is very efficient at reclaiming unused managed objects, but it is unable to reclaim unmanaged objects.

Dispose パターンには 2 種類あります。The dispose pattern has two variations:

  • 型で使用する各アンマネージ リソースをセーフ ハンドル (つまり、System.Runtime.InteropServices.SafeHandle から派生したクラス) でラップします。You wrap each unmanaged resource that a type uses in a safe handle (that is, in a class derived from System.Runtime.InteropServices.SafeHandle). この場合、IDisposable インターフェイスと追加の Dispose(Boolean) メソッドを実装します。In this case, you implement the IDisposable interface and an additional Dispose(Boolean) method. これは推奨される方法で、Object.Finalize メソッドをオーバーライドする必要がありません。This is the recommended variation and doesn't require overriding the Object.Finalize method.

    注意

    Microsoft.Win32.SafeHandles 名前空間には SafeHandle から派生した一連のクラスが用意されています。これらのクラスの一覧については、「セーフ ハンドルの使用」を参照してください。The Microsoft.Win32.SafeHandles namespace provides a set of classes derived from SafeHandle, which are listed in the Using safe handles section. アンマネージ リソースを解放できるクラスが見つからない場合は、SafeHandle の独自のサブクラスを実装できます。If you can't find a class that is suitable for releasing your unmanaged resource, you can implement your own subclass of SafeHandle.

  • IDisposable インターフェイスと追加の Dispose(Boolean) メソッドを実装し、Object.Finalize メソッドもオーバーライドします。You implement the IDisposable interface and an additional Dispose(Boolean) method, and you also override the Object.Finalize method. Finalize の実装が型のコンシューマーによって呼び出されなかった場合にアンマネージ リソースが破棄されるように、IDisposable.Dispose をオーバーライドする必要があります。You must override Finalize to ensure that unmanaged resources are disposed of if your IDisposable.Dispose implementation is not called by a consumer of your type. 前の項目で説明された推奨される方法を使用すると、System.Runtime.InteropServices.SafeHandle クラスが代わりにこれを実行します。If you use the recommended technique discussed in the previous bullet, the System.Runtime.InteropServices.SafeHandle class does this on your behalf.

Dispose メソッドが複数回呼び出される場合でも、例外をスローすることなく呼び出されるようにして、リソースが常に適切にクリーンアップされるようにする必要があります。To help ensure that resources are always cleaned up appropriately, a Dispose method should be callable multiple times without throwing an exception.

GC.KeepAlive メソッドのコード例では、再利用するオブジェクトのメンバーがまだ実行中の場合でも、ガベージ コレクションがファイナライザーを実行しようとします。The code example provided for the GC.KeepAlive method shows how aggressive garbage collection can cause a finalizer to run while a member of the reclaimed object is still executing. 長い KeepAlive メソッドの最後に、Dispose メソッドを呼び出すことをお勧めします。It is a good idea to call the KeepAlive method at the end of a lengthy Dispose method.

Dispose() と Dispose(Boolean)Dispose() and Dispose(Boolean)

IDisposable インターフェイスでは、パラメーターのない Dispose メソッドを 1 つ実装する必要があります。The IDisposable interface requires the implementation of a single parameterless method, Dispose. しかし、Dispose パターンでは 2 種類の Dispose メソッドを実装する必要があります。However, the dispose pattern requires two Dispose methods to be implemented:

  • public で非仮想 (Visual Basic では NonInheritable) の IDisposable.Dispose の実装。パラメーターはありません。A public non-virtual (NonInheritable in Visual Basic) IDisposable.Dispose implementation that has no parameters.

  • protected で仮想 (Visual Basic では Overridable) の Dispose メソッド。シグネチャは次のとおりです。A protected virtual (Overridable in Visual Basic) Dispose method whose signature is:

    protected virtual void Dispose(bool disposing)
    
    Protected Overridable Sub Dispose(disposing As Boolean)
    

Dispose() オーバーロードThe Dispose() overload

この public で非仮想 (Visual Basic では NonInheritable)、パラメーターなしの Dispose メソッドは、型のコンシューマーによって呼び出されるため、その目的は、アンマネージ リソースを解放し、ファイナライザーが存在する場合、それを実行する必要がないことを示すことです。Because the public, non-virtual (NonInheritable in Visual Basic), parameterless Dispose method is called by a consumer of the type, its purpose is to free unmanaged resources and to indicate that the finalizer, if one is present, doesn't have to run. このため、次のような標準的な実装があります。Because of this, it has a standard implementation:

public void Dispose()
{
   // Dispose of unmanaged resources.
   Dispose(true);
   // Suppress finalization.
   GC.SuppressFinalize(this);
}
Public Sub Dispose() _
           Implements IDisposable.Dispose
   ' Dispose of unmanaged resources.
   Dispose(True)
   ' Suppress finalization.
   GC.SuppressFinalize(Me)
End Sub

Dispose メソッドはオブジェクトのクリーンアップをすべて実行するため、ガベージ コレクターはもはやオブジェクトの Object.Finalize のオーバーライドを呼び出す必要はありません。The Dispose method performs all object cleanup, so the garbage collector no longer needs to call the objects' Object.Finalize override. そこで、SuppressFinalize メソッドを呼び出して、ガベージ コレクターによるファイナライザーの実行を抑制します。Therefore, the call to the SuppressFinalize method prevents the garbage collector from running the finalizer. 型にファイナライザーがない場合、GC.SuppressFinalize を呼び出しても無効です。If the type has no finalizer, the call to GC.SuppressFinalize has no effect. アンマネージ リソースを解放する実際の作業は Dispose メソッドの 2 番目のオーバーロードによって実行されることに注意してください。Note that the actual work of releasing unmanaged resources is performed by the second overload of the Dispose method.

Dispose(Boolean) オーバーロードThe Dispose(Boolean) overload

2 番目のオーバーロードでは、disposing パラメーターは Boolean で、メソッドの呼び出し元が Dispose メソッドか (値は true)、それともファイナライザーか (値は false) を示します。In the second overload, the disposing parameter is a Boolean that indicates whether the method call comes from a Dispose method (its value is true) or from a finalizer (its value is false).

メソッドの本体は 2 つのコード ブロックで構成されます。The body of the method consists of two blocks of code:

  • アンマネージ リソースを解放するブロック。A block that frees unmanaged resources. このブロックは、disposing パラメーターの値に関係なく実行されます。This block executes regardless of the value of the disposing parameter.

  • マネージ リソースを解放する条件付きブロック。A conditional block that frees managed resources. このブロックは、disposing の値が true の場合に実行されます。This block executes if the value of disposing is true. 解放するマネージ リソースには、次のオブジェクトを含めることができます。The managed resources that it frees can include:

    IDisposable を実装するマネージ オブジェクト。Managed objects that implement IDisposable. 条件付きブロックを使用して Dispose の実装を呼び出すことができます。The conditional block can be used to call their Dispose implementation. セーフ ハンドルを使用してアンマネージ リソースをラップしている場合は、ここで SafeHandle.Dispose(Boolean) の実装を呼び出す必要があります。If you have used a safe handle to wrap your unmanaged resource, you should call the SafeHandle.Dispose(Boolean) implementation here.

    大量のメモリを消費するか、不足しているリソースを消費するマネージ オブジェクト。Managed objects that consume large amounts of memory or consume scarce resources. これらのオブジェクトを Dispose メソッドで明示的に解放すると、ガベージ コレクターによって非確定的に解放される場合よりも迅速に解放されます。Freeing these objects explicitly in the Dispose method releases them faster than if they were reclaimed non-deterministically by the garbage collector.

メソッドの呼び出し元がファイナライザーの場合 (つまり、disposingfalse の場合)、アンマネージ リソースを解放するコードだけが実行されます。If the method call comes from a finalizer (that is, if disposing is false), only the code that frees unmanaged resources executes. ガベージ コレクターが終了処理の際にマネージ オブジェクトを破棄する順序は定義されていないため、値 Dispose を指定した false オーバーロードを呼び出すことで、既に解放されている可能性のあるマネージ リソースをファイナライザーが解放しようとすることを防止できます。Because the order in which the garbage collector destroys managed objects during finalization is not defined, calling this Dispose overload with a value of false prevents the finalizer from trying to release managed resources that may have already been reclaimed.

基底クラスでの Dispose パターンの実装Implementing the dispose pattern for a base class

基底クラスで Dispose パターンを実装する場合、以下の項目を用意する必要があります。If you implement the dispose pattern for a base class, you must provide the following:

重要

Dispose() を実装し、 sealed (Visual Basic では NotInheritable) ではないすべてのベース クラスにこのパターンを実装してください。You should implement this pattern for all base classes that implement Dispose() and are not sealed (NotInheritable in Visual Basic).

  • Dispose メソッドを呼び出す Dispose(Boolean) の実装。A Dispose implementation that calls the Dispose(Boolean) method.

  • リソースを解放する実際の作業を実行する Dispose(Boolean) メソッド。A Dispose(Boolean) method that performs the actual work of releasing resources.

  • アンマネージ リソースをラップする SafeHandle から派生したクラス (推奨)、または、Object.Finalize メソッドのオーバーライド。Either a class derived from SafeHandle that wraps your unmanaged resource (recommended), or an override to the Object.Finalize method. SafeHandle クラスには、コーディングが不要なファイナライザーが用意されています。The SafeHandle class provides a finalizer that frees you from having to code one.

セーフ ハンドルを使用して基底クラスで Dispose パターンを実装する一般的なパターンを次に示します。Here's the general pattern for implementing the dispose pattern for a base class that uses a safe handle.

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.
         //
      }
      
      disposed = true;
   }
}
Imports Microsoft.Win32.SafeHandles
Imports System.Runtime.InteropServices

Class BaseClass : Implements IDisposable
   ' Flag: Has Dispose already been called?
   Dim disposed As Boolean = False
   ' Instantiate a SafeHandle instance.
   Dim handle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True)

   ' Public implementation of Dispose pattern callable by consumers.
   Public Sub Dispose() _
              Implements IDisposable.Dispose
      Dispose(True)
      GC.SuppressFinalize(Me)           
   End Sub
   
   ' Protected implementation of Dispose pattern.
   Protected Overridable Sub Dispose(disposing As Boolean)
      If disposed Then Return
      
      If disposing Then
         handle.Dispose()
         ' Free any other managed objects here.
         '
      End If
      
      disposed = True
   End Sub
End Class

注意

前の例では、SafeFileHandle オブジェクトを使用してパターンを示しています。代わりに、SafeHandle から派生した任意のオブジェクトを使用することもできます。The previous example uses a SafeFileHandle object to illustrate the pattern; any object derived from SafeHandle could be used instead. 例では、SafeFileHandle オブジェクトを正しくインスタンス化していないことに注意してください。Note that the example does not properly instantiate its SafeFileHandle object.

Object.Finalize をオーバーライドして基底クラスで Dispose パターンを実装する一般的なパターンを次に示します。Here's the general pattern for implementing the dispose pattern for a base class that overrides Object.Finalize.

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);
   }
}
Class BaseClass : Implements IDisposable
   ' Flag: Has Dispose already been called?
   Dim disposed As Boolean = False
   
   ' Public implementation of Dispose pattern callable by consumers.
   Public Sub Dispose() _
              Implements IDisposable.Dispose
      Dispose(True)
      GC.SuppressFinalize(Me)           
   End Sub
   
   ' Protected implementation of Dispose pattern.
   Protected Overridable Sub Dispose(disposing As Boolean)
      If disposed Then Return
      
      If disposing Then
         ' Free any other managed objects here.
         '
      End If
      
      ' Free any unmanaged objects here.
      '
      disposed = True
   End Sub

   Protected Overrides Sub Finalize()
      Dispose(False)      
   End Sub
End Class

注意

C# では、デストラクターを定義することによって、Object.Finalize をオーバーライドします。In C#, you override Object.Finalize by defining a destructor.

派生クラスでの Dispose パターンの実装Implementing the dispose pattern for a derived class

IDisposable インターフェイスを実装するクラスから派生したクラスは、IDisposable の基底クラスでの実装が派生クラスに継承されるため、IDisposable.Dispose を実装しないでください。A class derived from a class that implements the IDisposable interface shouldn't implement IDisposable, because the base class implementation of IDisposable.Dispose is inherited by its derived classes. 代わりに、派生クラスで Dispose パターンを実装するには、以下の項目を用意します。Instead, to implement the dispose pattern for a derived class, you provide the following:

  • 基底クラスのメソッドをオーバーライドして、派生クラスのリソースを解放する実際の作業を実行する protected Dispose(Boolean) メソッド。A protected Dispose(Boolean) method that overrides the base class method and performs the actual work of releasing the resources of the derived class. このメソッドは、基底クラスの Dispose(Boolean) メソッドも呼び出して、それに disposing 引数の値として true を渡す必要があります。This method should also call the Dispose(Boolean) method of the base class and pass it a value of true for the disposing argument.

  • アンマネージ リソースをラップする SafeHandle から派生したクラス (推奨)、または、Object.Finalize メソッドのオーバーライド。Either a class derived from SafeHandle that wraps your unmanaged resource (recommended), or an override to the Object.Finalize method. SafeHandle クラスには、コーディングが不要なファイナライザーが用意されています。The SafeHandle class provides a finalizer that frees you from having to code one. ファイナライザーを用意する場合は、disposing 引数を false として Dispose(Boolean) オーバーロードを呼び出す必要があります。If you do provide a finalizer, it should call the Dispose(Boolean) overload with a disposing argument of false.

セーフ ハンドルを使用して派生クラスで Dispose パターンを実装する一般的なパターンを次に示します。Here's the general pattern for implementing the dispose pattern for a derived class that uses a safe handle:

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);
   }
}
Imports Microsoft.Win32.SafeHandles
Imports System.Runtime.InteropServices

Class DerivedClass : Inherits BaseClass 
   ' Flag: Has Dispose already been called?
   Dim disposed As Boolean = False
   ' Instantiate a SafeHandle instance.
   Dim handle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True)

   ' Protected implementation of Dispose pattern.
   Protected Overrides Sub Dispose(disposing As Boolean)
      If disposed Then Return
      
      If disposing Then
         handle.Dispose()
         ' Free any other managed objects here.
         '
      End If
      
      ' Free any unmanaged objects here.
      '
      disposed = True
      
      ' Call base class implementation.
      MyBase.Dispose(disposing)
   End Sub
End Class

注意

前の例では、SafeFileHandle オブジェクトを使用してパターンを示しています。代わりに、SafeHandle から派生した任意のオブジェクトを使用することもできます。The previous example uses a SafeFileHandle object to illustrate the pattern; any object derived from SafeHandle could be used instead. 例では、SafeFileHandle オブジェクトを正しくインスタンス化していないことに注意してください。Note that the example does not properly instantiate its SafeFileHandle object.

Object.Finalize をオーバーライドして派生クラスで Dispose パターンを実装する一般的なパターンを次に示します。Here's the general pattern for implementing the dispose pattern for a derived class that overrides Object.Finalize:

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);
   }
}
Class DerivedClass : Inherits BaseClass
   ' Flag: Has Dispose already been called?
   Dim disposed As Boolean = False
   
   ' Protected implementation of Dispose pattern.
   Protected Overrides Sub Dispose(disposing As Boolean)
      If disposed Then Return
      
      If disposing Then
         ' Free any other managed objects here.
         '
      End If
      
      ' Free any unmanaged objects here.
      '
      disposed = True

      ' Call the base class implementation.
      MyBase.Dispose(disposing)
   End Sub
   
   Protected Overrides Sub Finalize()
      Dispose(False)
   End Sub 
End Class

注意

C# では、デストラクターを定義することによって、Object.Finalize をオーバーライドします。In C#, you override Object.Finalize by defining a destructor.

セーフ ハンドルの使用Using safe handles

オブジェクトのファイナライザーのコードを記述することは、正しく行わないと問題が発生する可能性がある複雑なタスクです。Writing code for an object's finalizer is a complex task that can cause problems if not done correctly. そのため、ファイナライザーを実装するのではなく、System.Runtime.InteropServices.SafeHandle オブジェクトを構築することをお勧めします。Therefore, we recommend that you construct System.Runtime.InteropServices.SafeHandle objects instead of implementing a finalizer.

System.Runtime.InteropServices.SafeHandle クラスから派生したクラスは、処理を中断することなくハンドルの割り当てと解放を行うことで、オブジェクトの有効期間に関する問題を単純化します。Classes derived from the System.Runtime.InteropServices.SafeHandle class simplify object lifetime issues by assigning and releasing handles without interruption. セーフ ハンドルは、アプリケーション ドメインのアンロード中に確実に実行されるクリティカル ファイナライザーを含んでいます。They contain a critical finalizer that is guaranteed to run while an application domain is unloading. セーフ ハンドルを使用する利点の詳細については、「System.Runtime.InteropServices.SafeHandle」を参照してください。For more information about the advantages of using a safe handle, see System.Runtime.InteropServices.SafeHandle. Microsoft.Win32.SafeHandles 名前空間の次の派生クラスは、セーフ ハンドルを提供します。The following derived classes in the Microsoft.Win32.SafeHandles namespace provide safe handles:

セーフ ハンドルを使用した基底クラスでの Dispose パターンの実装Using a safe handle to implement the dispose pattern for a base class

次の例は、セーフ ハンドルを使用してアンマネージ リソースをカプセル化する、基底クラス DisposableStreamResource での Dispose パターンを示します。The following example illustrates the dispose pattern for a base class, DisposableStreamResource, that uses a safe handle to encapsulate unmanaged resources. 例では、DisposableResource を使用して、開いているファイルを表す SafeFileHandle オブジェクトをラップする Stream クラスを定義しています。It defines a DisposableResource class that uses a SafeFileHandle to wrap a Stream object that represents an open file. DisposableResource メソッドには、ファイル ストリームの合計バイト数を返す Size プロパティも含まれています。The DisposableResource method also includes a single property, Size, that returns the total number of bytes in the file stream.

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;
   }  
}
Imports Microsoft.Win32.SafeHandles
Imports System.IO

Public Class DisposableStreamResource : Implements IDisposable
   ' Define constants.
   Protected Const GENERIC_READ As UInteger = &H80000000ui
   Protected Const FILE_SHARE_READ As UInteger = &H0000000i
   Protected Const OPEN_EXISTING As UInteger = 3
   Protected Const FILE_ATTRIBUTE_NORMAL As UInteger = &H80
   Protected INVALID_HANDLE_VALUE As New IntPtr(-1)
   Private Const INVALID_FILE_SIZE As Integer = &HFFFFFFFF
   
   ' Define Windows APIs.
   Protected Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (
                                         lpFileName As String, dwDesiredAccess As UInt32, 
                                         dwShareMode As UInt32, lpSecurityAttributes As IntPtr, 
                                         dwCreationDisposition As UInt32, dwFlagsAndAttributes As UInt32, 
                                         hTemplateFile As IntPtr) As IntPtr
   Private Declare Function GetFileSize Lib "kernel32" (hFile As SafeFileHandle, 
                                                        ByRef lpFileSizeHigh As Integer) As Integer
    
   ' Define locals.
   Private disposed As Boolean = False
   Private safeHandle As SafeFileHandle 
   Private bufferSize As Long 
   Private upperWord As Integer
   
   Public Sub New(filename As String)
      If filename Is Nothing Then
         Throw New ArgumentNullException("The filename cannot be null.")
      Else If filename = ""
         Throw New ArgumentException("The filename cannot be an empty string.")
      End If
            
      Dim handle As IntPtr = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ,
                                        IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
                                        IntPtr.Zero)
      If handle <> INVALID_HANDLE_VALUE Then
         safeHandle = New SafeFileHandle(handle, True)
      Else
         Throw New FileNotFoundException(String.Format("Cannot open '{0}'", filename))
      End If
      
      ' Get file size.
      bufferSize = GetFileSize(safeHandle, upperWord) 
      If bufferSize = INVALID_FILE_SIZE Then
         bufferSize = -1
      Else If upperWord > 0 Then 
         bufferSize = (CLng(upperWord) << 32) + bufferSize
      End If     
   End Sub
   
   Public ReadOnly Property Size As Long
      Get
         Return bufferSize
      End Get
   End Property
   
   Public Sub Dispose() _
              Implements IDisposable.Dispose
      Dispose(True)
      GC.SuppressFinalize(Me)
   End Sub           

   Protected Overridable Sub Dispose(disposing As Boolean)
      If disposed Then Exit Sub

      ' Dispose of managed resources here.
      If disposing Then
         safeHandle.Dispose()
      End If
      
      ' Dispose of any unmanaged resources not wrapped in safe handles.
      
      disposed = True
   End Sub  
End Class

セーフ ハンドルを使用した派生クラスでの Dispose パターンの実装Using a safe handle to implement the dispose pattern for a derived class

次の例は、前の例で挙げた DisposableStreamResource2 クラスを継承した派生クラス DisposableStreamResource での Dispose パターンを示します。The following example illustrates the dispose pattern for a derived class, DisposableStreamResource2, that inherits from the DisposableStreamResource class presented in the previous example. このクラスは WriteFileInfo メソッドを追加し、SafeFileHandle オブジェクトを使用して書き込み可能ファイル ハンドルをラップしています。The class adds an additional method, WriteFileInfo, and uses a SafeFileHandle object to wrap the handle of the writable file.

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);
   }
}
Imports Microsoft.Win32.SafeHandles
Imports System.IO

Public Class DisposableStreamResource2 : Inherits DisposableStreamResource
   ' Define additional constants.
   Protected Const GENERIC_WRITE As Integer = &H40000000 
   Protected Const OPEN_ALWAYS As Integer = 4
   
   ' Define additional APIs.
   Protected Declare Function WriteFile Lib "kernel32.dll" (
                              safeHandle As SafeFileHandle, lpBuffer As String, 
                              nNumberOfBytesToWrite As Integer, ByRef lpNumberOfBytesWritten As Integer,
                              lpOverlapped As Object) As Boolean
   
   ' Define locals.
   Private disposed As Boolean = False
   Private filename As String
   Private created As Boolean = False
   Private safeHandle As SafeFileHandle
   
   Public Sub New(filename As String)
      MyBase.New(filename)
      Me.filename = filename
   End Sub
   
   Public Sub WriteFileInfo() 
      If Not created Then
         Dim hFile As IntPtr = CreateFile(".\FileInfo.txt", GENERIC_WRITE, 0, 
                                          IntPtr.Zero, OPEN_ALWAYS, 
                                          FILE_ATTRIBUTE_NORMAL, IntPtr.Zero)
         If hFile <> INVALID_HANDLE_VALUE Then
            safeHandle = New SafeFileHandle(hFile, True)
         Else
            Throw New IOException("Unable to create output file.")
         End If
         created = True
      End If
      Dim output As String = String.Format("{0}: {1:N0} bytes {2}", filename, Size, 
                                           vbCrLf)
      WriteFile(safeHandle, output, output.Length, 0&, Nothing)                                     
   End Sub

   Protected Overloads Overridable Sub Dispose(disposing As Boolean)
      If disposed Then Exit Sub
      
      ' Release any managed resources here.
      If disposing Then
         safeHandle.Dispose()
      End If
      
      disposed = True
      ' Release any unmanaged resources not wrapped by safe handles here.
      
      ' Call the base class implementation.
      MyBase.Dispose(True)
   End Sub
End Class

関連項目See also

SuppressFinalize
IDisposable
IDisposable.Dispose
Microsoft.Win32.SafeHandles
System.Runtime.InteropServices.SafeHandle
Object.Finalize
方法: クラスと構造体を定義および使用する (C++/CLI) How to: Define and Consume Classes and Structs (C++/CLI)
Dispose パターンDispose Pattern