Share via


Implementar un método Dispose

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. El modelo de Dispose solo se usa para los objetos que tienen acceso a recursos no administrados. Esto se debe a que el recolector de elementos no utilizados es muy eficaz al recuperar objetos administrados no usados.

El método Dispose de un tipo debe liberar todos los recursos de su propiedad. También debe liberar todos los recursos que posean sus tipos base; para ello, llama al método Dispose de su tipo primario. El método Dispose del tipo primario debe liberar todos los recursos que posee y, a su vez, llamar al método Dispose de su tipo primario, de modo que este patrón se propaga por la jerarquía de tipos base. 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.

No supone ninguna ventaja sobre el rendimiento implementar el método Dispose en tipos que utilizan sólo recursos administrados (como matrices) porque el recolector de elementos no utilizados los reclama automáticamente. Utilice principalmente el método Dispose en los objetos administrados que utilizan los recursos nativos y en los objetos COM que se exponen a .NET Framework. Los objetos administrados que utilizan recursos nativos (como la clase FileStream) implementan la interfaz IDisposable.

Nota importanteImportante

Los programadores de C++ no deben utilizar este tema.En su lugar, vea Destructors and Finalizers in Visual C++.En .NET Framework versión 2.0, el compilador de C++ proporciona compatibilidad para implementar la eliminación determinística de los recursos y no permite la implementación directa del método Dispose.

Un método Dispose debería llamar al método SuppressFinalize en el objeto que está desechando. Si el objeto se encuentra en la cola de finalización en ese momento, SuppressFinalize evita que se llame a su método Finalize. Recuerde que ejecutar un método Finalize afecta al rendimiento. Si el método Dispose ya ha limpiado el objeto, no es necesario que el recolector de elementos no utilizados llame al método Finalize del objeto.

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. Suele ser recomendable llamar al método KeepAlive al final de un método Dispose prolongado.

La alternativa SafeHandle

La escritura de código para el finalizador de un objeto es una tarea compleja que puede producir problemas si no se realiza correctamente. Por tanto, se recomienda construir objetos SafeHandle en lugar de implementar el modelo de eliminación.

La clase SafeHandle simplifica los problemas de duración de objetos asignando y liberando identificadores sin interrupción. Contiene un finalizador crítico cuya ejecución está garantizada mientras se descarga un dominio de aplicación. Para obtener más información sobre las ventajas de usar un controlador seguro, vea Identificadores seguros y finalización crítica.

La clase SafeHandle del espacio de nombres System.Runtime.InteropServices es una clase contenedora abstracta para los identificadores del sistema operativo. Es difícil derivar de esta clase. En su lugar, use las clases derivadas del espacio de nombres Microsoft.Win32.SafeHandles que proporcionan controladores seguros para lo siguiente:

  • Archivos y canalizaciones.

  • Vistas de memoria.

  • Construcciones de criptografía.

  • Claves del Registro.

  • Identificadores de espera.

Ejemplo

En el ejemplo de código siguiente se muestra el modelo de diseño recomendado para implementar un método Dispose para las clases que encapsulan recursos no administrados.

Las clases de recursos se derivan normalmente de clases nativas complejas o de las API y se deben personalizar de manera conveniente. Utilice este patrón de código como punto de partida para la creación de una clase de recursos y proporcione la personalización necesaria de acuerdo con los recursos que se encapsulan.

Imports System
Imports System.IO
Class Program

    Public Shared Sub Main()
        Try
            ' Initialize a Stream resource to pass 
            ' to the DisposableResource class.
           Console.Write("Enter filename and its path: ")
            Dim fileSpec As String = Console.ReadLine
            Dim fs As FileStream = File.OpenRead(fileSpec)
            Dim TestObj As DisposableResource = New DisposableResource(fs)

            ' Use the resource.
            TestObj.DoSomethingWithResource()

            ' Dispose theresource.
            TestObj.Dispose()

        Catch e As FileNotFoundException
            Console.WriteLine(e.Message)
        End Try
    End Sub
End Class

' This class shows how to use a disposable resource.
' The resource is first initialized and passed to
' the constructor, but it could also be
' initialized in the constructor.
' The lifetime of the resource does not 
' exceed the lifetime of this instance.
' This type does not need a finalizer because it does not
' directly create a native resource like a file handle
' or memory in the unmanaged heap.
Public Class DisposableResource
    Implements IDisposable

    Private _resource As Stream

    Private _disposed As Boolean

    ' The stream passed to the constructor
    ' must be readable and not null.
    Public Sub New(ByVal stream As Stream)
        MyBase.New()
        If (stream Is Nothing) Then
            Throw New ArgumentNullException("Stream is null.")
        End If
        If Not stream.CanRead Then
            Throw New ArgumentException("Stream must be readable.")
        End If
        _resource = stream
        Dim objTypeName As String = _resource.GetType.ToString
        _disposed = False
    End Sub

    ' Demonstrates using the resource.
    ' It must not be already disposed.
    Public Sub DoSomethingWithResource()
        If _disposed Then
            Throw New ObjectDisposedException("Resource was disposed.")
        End If

        ' Show the number of bytes.
        Dim numBytes As Integer = CType(_resource.Length, Integer)
        Console.WriteLine("Number of bytes: {0}", numBytes.ToString)
    End Sub

    Public Overloads Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)

        ' Use SupressFinalize in case a subclass
        ' of this type implements a finalizer.
        GC.SuppressFinalize(Me)
    End Sub

    Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)
        If Not _disposed Then

            ' If you need thread safety, use a lock around these 
            ' operations, as well as in your methods that use the resource.
            If disposing Then
                If (Not (_resource) Is Nothing) Then
                    _resource.Dispose()
                End If
                Console.WriteLine("Object disposed.")
            End If

            ' Indicates that the instance has been disposed.
            _resource = Nothing
            _disposed = True
        End If
    End Sub
End Class
using System;
using System.IO;

class Program
{

    static void Main()
    {
        try
        {
            // Initialize a Stream resource to pass 
            // to the DisposableResource class.
            Console.Write("Enter filename and its path: ");
            string fileSpec = Console.ReadLine();
            FileStream fs = File.OpenRead(fileSpec);
            DisposableResource TestObj = new DisposableResource(fs);

            // Use the resource.
            TestObj.DoSomethingWithResource();

            // Dispose the resource.
            TestObj.Dispose();

        }
        catch (FileNotFoundException e)
        {
            Console.WriteLine(e.Message);
        }
    }
}


// This class shows how to use a disposable resource.
// The resource is first initialized and passed to
// the constructor, but it could also be
// initialized in the constructor.
// The lifetime of the resource does not 
// exceed the lifetime of this instance.
// This type does not need a finalizer because it does not
// directly create a native resource like a file handle
// or memory in the unmanaged heap.

public class DisposableResource : IDisposable
{

    private Stream _resource;  
    private bool _disposed;

    // The stream passed to the constructor 
    // must be readable and not null.
    public DisposableResource(Stream stream)
    {
        if (stream == null)
            throw new ArgumentNullException("Stream in null.");
        if (!stream.CanRead)
            throw new ArgumentException("Stream must be readable.");

        _resource = stream;

        _disposed = false;
    }

    // Demonstrates using the resource. 
    // It must not be already disposed.
    public void DoSomethingWithResource() {
        if (_disposed)
            throw new ObjectDisposedException("Resource was disposed.");

        // Show the number of bytes.
        int numBytes = (int) _resource.Length;
        Console.WriteLine("Number of bytes: {0}", numBytes.ToString());
    }


    public void Dispose() 
    {
        Dispose(true);

        // Use SupressFinalize in case a subclass
        // of this type implements a finalizer.
        GC.SuppressFinalize(this);      
    }

    protected virtual void Dispose(bool disposing)
    {
        // If you need thread safety, use a lock around these 
        // operations, as well as in your methods that use the resource.
        if (!_disposed)
        {
            if (disposing) {
                if (_resource != null)
                    _resource.Dispose();
                    Console.WriteLine("Object disposed.");
            }

            // Indicate that the instance has been disposed.
            _resource = null;
            _disposed = true;   
        }
    }
}

Vea también

Referencia

SuppressFinalize

Destructors and Finalizers in Visual C++

Implementar Finalize y Dispose para limpiar recursos no administrados

Conceptos

Reemplazar el método Finalize