Implementando um método Dispose

O padrão para descartar um objeto, conhecido como um padrão de dispose, impõe ordem no tempo de vida de um objeto. O padrão dispose é usado somente para objetos que acessam os recursos não gerenciados. Isso ocorre porque o coletor de lixo é muito eficiente em recuperar objetos gerenciados não utilizados.

Um tipo Disposemétodo deve liberar todos os recursos que ele possui. Ele também deve liberar todos os recursos pertencentes a seus tipos base chamando seu tipo de pai Dispose método. O tipo de pai Dispose método deve liberar todos os recursos que ele possui e chamar seu tipo de pai Dispose método, propagando esse padrão por meio da hierarquia de tipos base. Para ajudar a garantir que recursos são sempre limpos adequadamente, um Dispose método deve ser chamado várias vezes sem lançar uma exceção.

Não há nenhum benefício de desempenho na implementação de Dispose método em tipos que usam apenas recursos gerenciados (como arrays) porque eles são automaticamente recuperados pelo coletor de lixo. Use o Dispose método principalmente os objetos gerenciados que usam recursos nativos e objetos COM que são expostos para o.NET Framework. Gerenciado de objetos que usam recursos nativos (como o FileStream classe) implementar o IDisposable interface.

Observação importanteImportante

Programadores C++ não devem usar este tópico.Em vez disso, consulte Destructors and Finalizers in Visual C++.No.NET Framework versão 2.0, o compilador C++ oferece suporte para implementar determinista alienação de recursos e não permite a implementação direta da Dispose método.

A Dispose deve chamar o método de SuppressFinalize método para o objeto que ele está descartando. Se o objeto está atualmente na fila de finalização, SuppressFinalize impede que seu Finalize método seja chamado. Lembre-se de que executar um Finalize método é caro para o desempenho. Se sua Dispose método já trabalhou para limpar o objeto, em seguida, ele não seja necessário para o coletor de lixo chamar o objeto Finalize método.

O exemplo de código fornecido para o GC.KeepAlive método mostra agressividade lixo coleção pode causar um finalizador para execução enquanto um membro do objeto recuperado ainda está em execução. É uma boa idéia para chamar o KeepAlive método no final de uma longa Dispose método.

A alternativa de SafeHandle

Escrevendo código para o finalizador do objeto é uma tarefa complexa que pode causar problemas caso não seja feito corretamente. Portanto, recomendamos que você construir SafeHandle objetos em vez de implementar o padrão dispose.

O SafeHandle classe simplifica os problemas de tempo de vida do objeto atribuindo e liberando alças sem interrupção. Ele contém um finalizador crítico que é garantido para ser executado enquanto o domínio de aplicativo está descarregando. Para obter mais informações sobre as vantagens de usar um identificador de seguro, consulte Identificadores de segurança e crítica finalização.

O SafeHandle classe na System.Runtime.InteropServices espaço para nome é uma classe abstrata wrapper para identificadores do sistema operacional. É difícil de derivar desta classe. Em vez disso, use as classes derivadas de Microsoft.Win32.SafeHandles espaço para nome que fornecer os identificadores de segurança para o seguinte:

  • Os arquivos e pipes.

  • Modos de exibição de memória.

  • Construções de criptografia.

  • Chaves de registro.

  • Identificadores de espera.

Exemplo

O exemplo de código a seguir mostra o padrão de design recomendado para implementar um Dispose método para classes que encapsulam recursos não gerenciados.

Classes de recursos normalmente são derivadas de classes nativas complexa ou APIs e devem ser personalizados de acordo. Utilizem este padrão de código como ponto de partida para a criação de uma classe de recurso e fornecer a personalização necessária com base nos recursos que são de encapsulamento.

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

Consulte também

Referência

SuppressFinalize

Destructors and Finalizers in Visual C++

Implementando finalizar e Dispose para limpeza de recursos não gerenciados

Conceitos

Substituindo o método de finalizar