Implementar un método DisposeImplementing a Dispose method

El método Dispose se implementa para liberar recursos no administrados que la aplicación usa.You implement a Dispose method to release unmanaged resources used by your application. El recolector de elementos no utilizados de .NET no asigna ni libera memoria no administrada.The .NET garbage collector does not allocate or release unmanaged memory.

El modelo para desechar un objeto, lo que se conoce como modelo de Dispose, sirve para imponer orden sobre la duración de un objeto.The pattern for disposing an object, referred to as a dispose pattern, imposes order on the lifetime of an object. El patrón de Dispose se utiliza solo con los objetos que tienen acceso a recursos no administrados, como identificadores de archivo y de canalización, identificadores de registro, identificadores de espera o punteros a bloques de memoria sin administrar.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. Esto se debe a que el recolector de elementos no utilizados es muy eficaz a la hora de reclamar objetos administrados no usados, aunque no puede reclamar objetos no administrados.This is because the garbage collector is very efficient at reclaiming unused managed objects, but it is unable to reclaim unmanaged objects.

El patrón de Dispose tiene dos variaciones:The dispose pattern has two variations:

Para asegurarse de que los recursos se limpien siempre correctamente, un método Dispose debe debe ser invocable varias veces sin que se produzca una excepción.To help ensure that resources are always cleaned up appropriately, a Dispose method should be callable multiple times without throwing an exception.

El ejemplo de código proporcionado para el método GC.KeepAlive muestra cómo la recolección de elementos no utilizados rigurosa puede hacer que se ejecute un finalizador mientras un miembro del objeto reclamado todavía se está ejecutando.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. Suele ser recomendable llamar al método KeepAlive al final de un método Dispose prolongado.It is a good idea to call the KeepAlive method at the end of a lengthy Dispose method.

Dispose() y Dispose (booleano)Dispose() and Dispose(Boolean)

La interfaz IDisposable requiere la implementación de un único método sin parámetros, Dispose.The IDisposable interface requires the implementation of a single parameterless method, Dispose. Sin embargo, el patrón de Dispose requiere dos métodos Dispose para implementarse:However, the dispose pattern requires two Dispose methods to be implemented:

  • Una implementación pública que no sea virtual (NonInheritable en Visual Basic) IDisposable.Dispose y que no tenga parámetros.A public non-virtual (NonInheritable in Visual Basic) IDisposable.Dispose implementation that has no parameters.

  • Un método protegido virtual (Overridable in Visual Basic) Dispose cuya signatura es: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)
    

La sobrecarga Dispose()The Dispose() overload

Dado que un consumidor del tipo llama a este método NonInheritable público, no virtual (Dispose en Visual Basic) y sin parámetros, su propósito consiste en liberar recursos no administrados e indicar que el finalizador, si existe, no tiene que ejecutarse.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. Debido a esto, se realiza una implementación estándar: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

El método Dispose limpia todos los objetos, por lo que el recolector de elementos no utilizados no necesita llamar a la invalidación Object.Finalize de los objetos.The Dispose method performs all object cleanup, so the garbage collector no longer needs to call the objects' Object.Finalize override. Por consiguiente, la llamada al método SuppressFinalize evita que el recolector de elementos no utilizados ejecute el finalizador.Therefore, the call to the SuppressFinalize method prevents the garbage collector from running the finalizer. Si el tipo no tiene ningún finalizador, la llamada a GC.SuppressFinalize no tiene ningún efecto.If the type has no finalizer, the call to GC.SuppressFinalize has no effect. Observe que el trabajo real de liberar recursos no administrados lo realiza la segunda sobrecarga del método Dispose.Note that the actual work of releasing unmanaged resources is performed by the second overload of the Dispose method.

La sobrecarga Dispose(Boolean)The Dispose(Boolean) overload

En la segunda sobrecarga, el parámetro disposing es un valor Boolean que indica si la llamada al método procede de un método Dispose (su valor es true) o de un finalizador (su valor es 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).

El cuerpo del método consta de dos bloques de código:The body of the method consists of two blocks of code:

  • Un bloque que libera los recursos no administrados.A block that frees unmanaged resources. Este bloque se ejecuta independientemente del valor del parámetro disposing.This block executes regardless of the value of the disposing parameter.

  • Un bloque condicional que libera los recursos administrados.A conditional block that frees managed resources. Este bloque se ejecuta si el valor de disposing es true.This block executes if the value of disposing is true. Estos son algunos de los recursos administrados que se liberan:The managed resources that it frees can include:

    Objetos administrados que implementan IDisposable.Managed objects that implement IDisposable. El bloque condicional se puede utilizar para llamar a la implementación Dispose.The conditional block can be used to call their Dispose implementation. Si ha utilizado un controlador seguro para incluir el recurso no administrado, debe llamar aquí a la implementación SafeHandle.Dispose(Boolean).If you have used a safe handle to wrap your unmanaged resource, you should call the SafeHandle.Dispose(Boolean) implementation here.

    Objetos administrados que consumen gran cantidad de memoria o recursos insuficientes.Managed objects that consume large amounts of memory or consume scarce resources. Al liberar estos objetos explícitamente en el método Dispose, se liberan más rápido que si el recolector de elementos no utilizados los reclamara de forma no determinista.Freeing these objects explicitly in the Dispose method releases them faster than if they were reclaimed non-deterministically by the garbage collector.

Si la llamada al método procede de un finalizador (es decir, disposing es false), solo se ejecuta el código que libera los recursos no administrados.If the method call comes from a finalizer (that is, if disposing is false), only the code that frees unmanaged resources executes. Como no se define el orden en que el recolector de elementos no utilizados destruye los objetos administrados durante la finalización, la llamada a esta sobrecarga Dispose con un valor de false evita que el finalizador intente liberar los recursos administrados que ya se hayan reclamado.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.

Implementación del patrón de Dispose para una clase baseImplementing the dispose pattern for a base class

Cuando se implementa el patrón de Dispose para una clase base, debe proporcionar lo siguiente:If you implement the dispose pattern for a base class, you must provide the following:

Importante

Implemente este patrón para todas las clases base que implementen Dispose() y no sean sealed (NotInheritable en Visual Basic).You should implement this pattern for all base classes that implement Dispose() and are not sealed (NotInheritable in Visual Basic).

  • Una implementación Dispose que llame al método Dispose(Boolean).A Dispose implementation that calls the Dispose(Boolean) method.

  • Un método Dispose(Boolean) que realiza el trabajo real de liberar recursos.A Dispose(Boolean) method that performs the actual work of releasing resources.

  • Una clase derivada de SafeHandle que contiene el recurso no administrado (recomendado), o una invalidación del método Object.Finalize.Either a class derived from SafeHandle that wraps your unmanaged resource (recommended), or an override to the Object.Finalize method. La clase SafeHandle proporciona un finalizador que evita que tenga que codificar uno.The SafeHandle class provides a finalizer that frees you from having to code one.

A continuación se muestra el patrón general para implementar el patrón de Dispose para una clase base que utiliza un controlador seguro.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

Nota

El ejemplo anterior utiliza un objeto SafeFileHandle para ilustrar el patrón; cualquier objeto derivado de SafeHandle podría usarse en su lugar.The previous example uses a SafeFileHandle object to illustrate the pattern; any object derived from SafeHandle could be used instead. Tenga en cuenta que el ejemplo no crea una instancia de su objeto SafeFileHandle correctamente.Note that the example does not properly instantiate its SafeFileHandle object.

A continuación se muestra el patrón general para implementar el patrón de Dispose para una clase base que invalide a Object.Finalize.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

Nota

En C#, invalida a Object.Finalize definiendo un destructor.In C#, you override Object.Finalize by defining a destructor.

Implementación del patrón de Dispose para una clase derivadaImplementing the dispose pattern for a derived class

Una clase derivada de una clase que implemente la interfaz IDisposable no debe implementar IDisposable, porque la implementación de la clase base de IDisposable.Dispose la heredan sus clases derivadas.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. En su lugar, para implementar el patrón de Dispose para una clase derivada, debe proporcionar lo siguiente:Instead, to implement the dispose pattern for a derived class, you provide the following:

  • Un método protected Dispose(Boolean) que invalide el método de la clase base y realice el trabajo real de liberar los recursos de la clase derivada.A protected Dispose(Boolean) method that overrides the base class method and performs the actual work of releasing the resources of the derived class. Este método también debe llamar al método Dispose(Boolean) de la clase base y pasar su estado disposing para el argumento.This method should also call the Dispose(Boolean) method of the base class and pass its disposing status for the argument.

  • Una clase derivada de SafeHandle que contiene el recurso no administrado (recomendado), o una invalidación del método Object.Finalize.Either a class derived from SafeHandle that wraps your unmanaged resource (recommended), or an override to the Object.Finalize method. La clase SafeHandle proporciona un finalizador que evita que tenga que programar uno.The SafeHandle class provides a finalizer that frees you from having to code one. Si proporciona un finalizador, debe llamar a la sobrecarga de Dispose(Boolean) con un argumento disposing que sea false.If you do provide a finalizer, it should call the Dispose(Boolean) overload with a disposing argument of false.

A continuación se muestra el patrón general para implementar el patrón de Dispose para una clase derivada que utiliza un controlador seguro: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

Nota

El ejemplo anterior utiliza un objeto SafeFileHandle para ilustrar el patrón; cualquier objeto derivado de SafeHandle podría usarse en su lugar.The previous example uses a SafeFileHandle object to illustrate the pattern; any object derived from SafeHandle could be used instead. Tenga en cuenta que el ejemplo no crea una instancia de su objeto SafeFileHandle correctamente.Note that the example does not properly instantiate its SafeFileHandle object.

A continuación se muestra el patrón general para implementar el patrón de Dispose para una clase derivada que invalide a Object.Finalize: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

Nota

En C#, invalida a Object.Finalize definiendo un destructor.In C#, you override Object.Finalize by defining a destructor.

Uso de controladores segurosUsing safe handles

La escritura de código para el finalizador de un objeto es una tarea compleja que puede producir problemas si no se realiza correctamente.Writing code for an object's finalizer is a complex task that can cause problems if not done correctly. Por tanto, se recomienda construir objetos System.Runtime.InteropServices.SafeHandle en lugar de implementar un finalizador.Therefore, we recommend that you construct System.Runtime.InteropServices.SafeHandle objects instead of implementing a finalizer.

Las clases derivadas de la clase System.Runtime.InteropServices.SafeHandle simplifican los problemas de duración de objetos mediante la asignación y liberación de identificadores sin interrupción.Classes derived from the System.Runtime.InteropServices.SafeHandle class simplify object lifetime issues by assigning and releasing handles without interruption. Contienen un finalizador crítico cuya ejecución está garantizada mientras se descarga un dominio de aplicación.They contain a critical finalizer that is guaranteed to run while an application domain is unloading. Para obtener más información sobre las ventajas de usar un controlador seguro, vea System.Runtime.InteropServices.SafeHandle.For more information about the advantages of using a safe handle, see System.Runtime.InteropServices.SafeHandle. Las clases derivadas siguientes en el espacio de nombres Microsoft.Win32.SafeHandles proporcionan controladores seguros:The following derived classes in the Microsoft.Win32.SafeHandles namespace provide safe handles:

Uso de un controlador seguro para implementar el patrón de Dispose para una clase baseUsing a safe handle to implement the dispose pattern for a base class

En el ejemplo siguiente se muestra el patrón de Dispose para una clase base, DisposableStreamResource, que utiliza un controlador seguro para encapsular los recursos no administrados.The following example illustrates the dispose pattern for a base class, DisposableStreamResource, that uses a safe handle to encapsulate unmanaged resources. Define una clase DisposableResource que utiliza un SafeFileHandle para incluir un objeto Stream que representa un archivo abierto.It defines a DisposableResource class that uses a SafeFileHandle to wrap a Stream object that represents an open file. El método DisposableResource también incluye una propiedad única, Size, que devuelve el número total de bytes de la secuencia de archivos.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

Uso de un controlador seguro para implementar el patrón de Dispose para una clase derivadaUsing a safe handle to implement the dispose pattern for a derived class

En el ejemplo siguiente se muestra el patrón de Dispose para una clase derivada, DisposableStreamResource2, que se hereda de la clase DisposableStreamResource mostrada en el ejemplo anterior.The following example illustrates the dispose pattern for a derived class, DisposableStreamResource2, that inherits from the DisposableStreamResource class presented in the previous example. La clase agrega un método adicional, WriteFileInfo, y utiliza un objeto SafeFileHandle para incluir el identificador del archivo editable.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 override 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(disposing);
   }
}
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(disposing)
   End Sub
End Class

Vea tambiénSee also