Implementación de un método DisposeImplement a Dispose method

La implementación del método Dispose sirve principalmente para publicar recursos no administrados.Implementing the Dispose method is primarily for releasing unmanaged resources. Al trabajar con miembros de instancia que son implementaciones de IDisposable, es habitual hacer llamadas de Dispose en cascada.When working with instance members that are IDisposable implementations, it's common to cascade Dispose calls. Hay otras razones para implementar Dispose, por ejemplo, para liberar memoria que se ha asignado, quitar un elemento que se ha agregado a una colección o señalar la liberación de un bloqueo adquirido.There are additional reasons for implementing Dispose, for example, to free memory that was allocated, remove an item that was added to a collection, or signal the release of a lock that was acquired.

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 patrón de Dispose, sirve para imponer orden sobre la duración de un objeto.The pattern for disposing an object, referred to as the dispose pattern, imposes order on the lifetime of an object. El patrón de Dispose se utiliza solo con los objetos que implementan la inferfaz IDisposable, y es común al interactuar con 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 for objects that implement the IDisposable interface, and is common when interacting with 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 no puede reclamar objetos no administrados.This is because the garbage collector is unable to reclaim unmanaged objects.

Para asegurarse de que los recursos se limpien siempre correctamente, un método Dispose debe ser idempotente, de manera que sea 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 idempotent, such that it is callable multiple times without throwing an exception. Además, las siguientes invocaciones de Dispose no deben hacer nada.Furthermore, subsequent invocations of Dispose should do nothing.

El ejemplo de código proporcionado para el método GC.KeepAlive muestra cómo la recolección de elementos no utilizados puede hacer que un finalizador se ejecute mientras una referencia no administrada al objeto o a sus miembros todavía está en uso.The code example provided for the GC.KeepAlive method shows how garbage collection can cause a finalizer to run, while an unmanaged reference to the object or its members is still in use. Usar GC.KeepAlive tiene sentido para hacer que el objeto no sea válido para la recolección de elementos no utilizados desde el principio de la rutina actual y hasta el momento en que se llamó a este método.It may make sense to utilize GC.KeepAlive to make the object ineligible for garbage collection from the start of the current routine to the point where this method is called.

Identificadores segurosSafe 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.

Un System.Runtime.InteropServices.SafeHandle es un tipo administrado abstracto que contiene un System.IntPtr que identifica un recurso no administrado.A System.Runtime.InteropServices.SafeHandle is an abstract managed type that wraps an System.IntPtr that identifies an unmanaged resource. En Windows, puede identificar un identificador y, en UNIX, un descriptor de archivo.On Windows it might identify a handle while on Unix, a file descriptor. Proporciona toda la lógica necesaria para asegurarse de que este recurso se libera una vez y solo una vez, cuando se elimina SafeHandle o cuando se quitan todas las referencias a SafeHandle y se finaliza la instancia de SafeHandle.It provides all of the logic necessary to ensure that this resource is released once and only once, when the SafeHandle is disposed of or when all references to the SafeHandle have been dropped and the SafeHandle instance is finalized.

System.Runtime.InteropServices.SafeHandle es una clase base abstracta.The System.Runtime.InteropServices.SafeHandle is an abstract base class. Las clases derivadas proporcionan instancias específicas para diferentes tipos de identificadores.Derived classes provide specific instances for different kinds of handle. Estas clases derivadas validan qué valores de System.IntPtr se consideran no válidos y cómo liberar realmente el identificador.These derived classes validate what values for the System.IntPtr are considered invalid and how to actually free the handle. Por ejemplo, SafeFileHandle se deriva de SafeHandle para ajustar IntPtrs que identifican los identificadores o descriptores de archivos abiertos e invalida su método SafeHandle.ReleaseHandle() para cerrarlo (a través de la función close en Unix o la función CloseHandle en Windows).For example, SafeFileHandle derives from SafeHandle to wrap IntPtrs that identify open file handles/descriptors, and overrides its SafeHandle.ReleaseHandle() method to close it (via the close function on Unix or CloseHandle function on Windows). La mayoría de las API de las bibliotecas de .NET que crean un recurso no administrado lo encapsularán en SafeHandle y devolverán ese SafeHandle según sea necesario, en lugar de volver a entregar el puntero básico.Most APIs in .NET libraries that create an unmanaged resource will wrap it in a SafeHandle and return that SafeHandle to you as needed, rather than handing back the raw pointer. En situaciones en las que interactúe con un componente no administrado y obtenga IntPtr para un recurso no administrado, puede crear su propio tipo de SafeHandle para ajustarlo.In situations where you interact with an unmanaged component and get an IntPtr for an unmanaged resource, you can create your own SafeHandle type to wrap it. Como resultado, algunos tipos no SafeHandle necesitan implementar finalizadores; la mayoría de las implementaciones de patrón descartable solo terminan con el ajuste de otros recursos administrados, algunos de los cuales pueden ser SafeHandle.As a result, few non-SafeHandle types need to implement finalizers; most disposable pattern implementations only end up wrapping other managed resources, some of which may be SafeHandles.

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:

Dispose() y Dispose (booleano)Dispose() and Dispose(bool)

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. Además, cualquier clase no sellada debe tener un método de sobrecarga Dispose(bool) adicional que se va a implementar:Also, any non-sealed class should have an additional Dispose(bool) overload method to be implemented:

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

  • Un método protected virtual (Overridable en Visual Basic) de tipo Dispose cuya signatura sea: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)
    End Sub
    

    Importante

    El parámetro disposing debe ser false cuando se llama desde un finalizador y true cuando se llama desde el método IDisposable.Dispose.The disposing parameter should be false when called from a finalizer, and true when called from the IDisposable.Dispose method. En otras palabras, es true cuando se llama de forma determinista y false cuando se llama de forma no determinista.In other words, it is true when deterministically called and false when non-deterministically called.

Método Dispose()The Dispose() method

Dado que un consumidor del tipo llama a este métodoDispose public, no virtual (NonInheritable en Visual Basic) y sin parámetros, su propósito consiste en liberar recursos no administrados, realizar limpiezas generales 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, perform general cleanup, and to indicate that the finalizer, if one is present, doesn't have to run. La liberación de la memoria real asociada a un objeto administrado es siempre una tarea que corresponde al recolector de elementos no utilizados.Freeing the actual memory associated with a managed object is always the domain of the garbage collector. 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. Tenga en cuenta que la limpieza real se realiza mediante la sobrecarga del método Dispose(bool).Note that the actual cleanup is performed by the Dispose(bool) method overload.

Sobrecarga del método Dispose (bool)The Dispose(bool) method overload

En la 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 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 (eliminación en cascada).The conditional block can be used to call their Dispose implementation (cascade dispose). Si ha utilizado una clase derivada de System.Runtime.InteropServices.SafeHandle para ajustar el recurso no administrado, debe llamar aquí a la implementación SafeHandle.Dispose().If you have used a derived class of System.Runtime.InteropServices.SafeHandle to wrap your unmanaged resource, you should call the SafeHandle.Dispose() 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. Asigne referencias de objetos administrados grandes a null para aumentar la probabilidad de que no se pueda acceder a ellos.Assign large managed object references to null to make them more likely to be unreachable. Esto los libera más rápido que si se recuperaran de forma no determinista y se suele hacer fuera del bloque condicional.This releases them faster than if they were reclaimed non-deterministically, and this is usually done outside of the conditional block.

Si la llamada al método procede de un finalizador, solo se debe ejecutar el código que libera los recursos no administrados.If the method call comes from a finalizer, only the code that frees unmanaged resources should execute. El implementador es responsable de garantizar que la ruta de acceso falsa no interactúe con los objetos administrados que se pueden haber reclamado.The implementer is responsible for ensuring the the false path doesn't interact with managed objects that may have been reclaimed. Esto es importante porque el orden en el que el recolector de elementos no utilizados destruye los objetos administrados durante la finalización no es determinista.This is important because the order in which the garbage collector destroys managed objects during finalization is non-deterministic.

Llamadas de eliminación en cascadaCascade dispose calls

Si la clase posee un campo o una propiedad y su tipo implementa IDisposable, la propia clase contenedora también debe implementar IDisposable.If your class owns a field or property, and its type implements IDisposable, the containing class itself should also implement IDisposable. Una clase que crea instancias de una implementación de IDisposable y la almacena como un miembro de instancia, también es responsable de su limpieza.A class that instantiates an IDisposable implementation and storing it as an instance member, is also responsible for its cleanup. Esto ayuda a garantizar que los tipos descartables a los que se hace referencia tienen la oportunidad de realizar una limpieza determinista mediante el método Dispose.This is to help ensure that the referenced disposable types are given the opportunity to deterministically perform clean up through the Dispose method. En este ejemplo, la clase es sealed (o NotInheritable en Visual Basic).In this example, the class is sealed (or NotInheritable in Visual Basic).

public sealed class Foo : IDisposable
{
    private readonly IDisposable _bar;

    public Foo()
    {
        _bar = new Bar();
    }

    public void Dispose()
    {
        _bar?.Dispose();
    }
}
Public NotInheritable Class Foo
    Implements IDisposable

    Private ReadOnly _bar As IDisposable

    Public Sub New()
        _bar = New Bar()
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        _bar.Dispose()
    End Sub
End Class

Implementación del patrón DisposeImplement the dispose pattern

Todas las clases no selladas o (clases de Visual Basic no modificadas como NotInheritable) deben considerarse una clase base potencial, ya que se podrían heredar.All non-sealed classes or (Visual Basic classes not modified as NotInheritable) should be considered a potential base class, because they could be inherited. Cuando se implementa el patrón de Dispose para cualquier clase base potencial, debe proporcionar lo siguiente:If you implement the dispose pattern for any potential base class, you must provide the following:

  • Una implementación Dispose que llame al método Dispose(bool).A Dispose implementation that calls the Dispose(bool) method.
  • Un método Dispose(bool) que realiza la tarea real de limpieza.A Dispose(bool) method that performs the actual cleanup.
  • 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, por lo que no tiene que escribir uno personalmente.The SafeHandle class provides a finalizer, so you do not have to write one yourself.

Importante

Es posible que una clase base solo haga referencia a objetos administrados e implemente el patrón de Dispose.It is possible for a base class to only reference managed objects, and implement the dispose pattern. En estos casos, un finalizador no es necesario.In these cases, a finalizer is unnecessary. Un finalizador solo es necesario si se hace referencia directamente a los recursos no administrados.A finalizer is only required if you directly reference unmanaged resources.

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
{
    // To detect redundant calls
    private bool _disposed = false;

    // Instantiate a SafeHandle instance.
    private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true);

    // Public implementation of Dispose pattern callable by consumers.
    public void Dispose() => Dispose(true);

    // Protected implementation of Dispose pattern.
    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
        {
            return;
        }

        if (disposing)
        {
           // Dispose managed state (managed objects).
            _safeHandle?.Dispose();
        }

        _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
{
    // To detect redundant calls
    private bool _disposed = false;

    ~BaseClass() => Dispose(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)
        {
            // TODO: dispose managed state (managed objects).
        }

        // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
        // TODO: set large fields to null.

        _disposed = true;
    }
}
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

Sugerencia

En C#, se crea un finalizador invalidando Object.Finalize.In C#, you create a finalizer by overriding Object.Finalize. En Visual Basic, esto se hace con Protected Overrides Sub Finalize().In Visual Basic, this is done with Protected Overrides Sub Finalize().

Implementación del patrón de Dispose para una clase derivadaImplement 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 limpiar una clase derivada, debe proporcionar los siguientes elementos:Instead, to cleanup a derived class, you provide the following:

  • Un método protected override void Dispose(bool) que invalide el método de la clase base y realice la limpieza real de la clase derivada.A protected override void Dispose(bool) method that overrides the base class method and performs the actual cleanup of the derived class. Este método también debe llamar al método base.Dispose(bool) (MyBase.Dispose(bool) en Visual Basic) de la clase base y pasar su estado disposing para el argumento.This method must also call the base.Dispose(bool) (MyBase.Dispose(bool) in Visual Basic) 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 codificar 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(bool) con un argumento disposing que sea false.If you do provide a finalizer, it must call the Dispose(bool) 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
{
    // To detect redundant calls
    private bool _disposed = false;

    // Instantiate a SafeHandle instance.
    private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true);

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        if (_disposed)
        {
            return;
        }

        if (disposing)
        {
           // Dispose managed state (managed objects).
            _safeHandle?.Dispose();
        }

        _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
{
    // To detect redundant calls
    bool _disposed = false;

    ~DerivedClass() => Dispose(false);

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        if (_disposed)
        {
            return;
        }

        if (disposing)
        {
            // TODO: dispose managed state (managed objects).
        }

        // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
        // TODO: set large fields to null.
        _disposed = true;

        // Call the base class implementation.
        base.Dispose(disposing);
    }
}
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

Implementación del patrón de Dispose con identificadores segurosImplement the dispose pattern with safe handles

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 DisposableStreamResource que utiliza un SafeFileHandle para incluir un objeto Stream que representa un archivo abierto.It defines a DisposableStreamResource class that uses a SafeFileHandle to wrap a Stream object that represents an open file. La clase también incluye una propiedad única, Size, que devuelve el número total de bytes de la secuencia de archivos.The class 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.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;
    private const int INVALID_FILE_SIZE = unchecked((int)0xFFFFFFFF);

    // Define Windows APIs.
    [DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode)]
    protected static extern SafeFileHandle 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 readonly SafeFileHandle _safeHandle;
    private readonly int _upperWord;

    public DisposableStreamResource(string fileName)
    {
        if (string.IsNullOrWhiteSpace(fileName))
        {
            throw new ArgumentException("The fileName cannot be null or an empty string");
        }

        _safeHandle = CreateFile(
            fileName, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero,
            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);

        // Get file size.
        Size = GetFileSize(_safeHandle, out _upperWord);
        if (Size == INVALID_FILE_SIZE)
        {
            Size = -1;
        }
        else if (_upperWord > 0)
        {
            Size = (((long)_upperWord) << 32) + Size;
        }
    }

    public long Size { get; }

    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 = &H0I
    Protected Const OPEN_EXISTING As UInteger = 3
    Protected Const FILE_ATTRIBUTE_NORMAL As UInteger = &H80
    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 SafeFileHandle

    Private Declare Function GetFileSize Lib "kernel32" (
        hFile As SafeFileHandle, ByRef lpFileSizeHigh As Integer) As Integer

    ' Define locals.
    Private disposed As Boolean = False
    Private ReadOnly safeHandle As SafeFileHandle
    Private ReadOnly upperWord As Integer

    Public Sub New(fileName As String)
        If String.IsNullOrWhiteSpace(fileName) Then
            Throw New ArgumentNullException("The fileName cannot be null or an empty string")
        End If

        safeHandle = CreateFile(
            fileName, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero,
            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero)

        ' Get file size.
        Size = GetFileSize(safeHandle, upperWord)
        If Size = INVALID_FILE_SIZE Then
            Size = -1
        ElseIf upperWord > 0 Then
            Size = (CLng(upperWord) << 32) + Size
        End If
    End Sub

    Public ReadOnly Property Size As Long

    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

Implementación del patrón de Dispose para una clase derivada con identificadores segurosImplement the dispose pattern for a derived class with safe handles

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.Runtime.InteropServices;

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);

    // To detect redundant calls
    private bool _disposed = false;
    private bool _created = false;
    private SafeFileHandle _safeHandle;
    private readonly string _fileName;

    public DisposableStreamResource2(string fileName) : base(fileName) => _fileName = fileName;

    public void WriteFileInfo()
    {
        if (!_created)
        {
            _safeHandle = CreateFile(
                @".\FileInfo.txt", GENERIC_WRITE, 0, IntPtr.Zero,
                OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);

            _created = true;
        }

        string output = $"{_fileName}: {Size:N0} bytes\n";
        _ = WriteFile(_safeHandle, output, output.Length, out _, IntPtr.Zero);
    }

    protected override void Dispose(bool disposing)
    {
        if (_disposed)
        {
            return;
        }

        // Release any managed resources here.
        if (disposing)
        {
            // Dispose managed state (managed objects).
            _safeHandle?.Dispose();
        }

        // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
        // TODO: set large fields to null.

        _disposed = true;

        // 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 created As Boolean = False
    Private safeHandle As SafeFileHandle
    Private ReadOnly filename As String

    Public Sub New(filename As String)
        MyBase.New(filename)
        Me.filename = filename
    End Sub

    Public Sub WriteFileInfo()
        If Not created Then
            safeHandle = CreateFile(
                ".\FileInfo.txt", GENERIC_WRITE, 0, IntPtr.Zero,
                OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero)
            created = True
        End If

        Dim output As String = $"{filename }: {Size:N0} bytes {vbCrLf }"
        Dim result = WriteFile(safeHandle, output, output.Length, 0&, Nothing)
    End Sub

    Protected Overridable Overloads 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