Partilhar via


Usando objetos que implementam IDisposable

O coletor de lixo (GC) do Common Language Runtime recupera a memória usada por objetos gerenciados. Normalmente, os tipos que usam recursos não gerenciados implementam a IDisposable interface ou IAsyncDisposable para permitir que os recursos não gerenciados sejam recuperados. Ao terminar de usar um objeto que implementa IDisposableo , você chama a implementação ou DisposeDisposeAsync o objeto para executar explicitamente a limpeza. Pode fazer isso de duas maneiras:

  • Com a instrução ou declaração C# using (Using em Visual Basic).
  • Implementando um try/finally bloco e chamando o Dispose método or DisposeAsync no finally.

Importante

O GC não descarta seus objetos, pois não tem conhecimento de IDisposable.Dispose() ou IAsyncDisposable.DisposeAsync(). O GC só sabe se um objeto é finalizável (ou seja, define um Object.Finalize() método) e quando o finalizador do objeto precisa ser chamado. Para obter mais informações, consulte Como funciona a finalização. Para mais informações sobre a implementação Dispose e , consulte DisposeAsync:

Objetos que implementam System.IDisposable ou System.IAsyncDisposable devem ser sempre descartados corretamente, independentemente do escopo variável, salvo indicação explícita em contrário. Os tipos que definem um finalizador para liberar recursos não gerenciados geralmente são chamados GC.SuppressFinalize de sua Dispose implementação DisposeAsync . A chamada SuppressFinalize indica ao GC que o finalizador já foi executado e que o objeto não deve ser promovido para finalização.

A instrução using

A using instrução em C# e a Using instrução em Visual Basic simplificam o código que você deve escrever para limpar um objeto. A using instrução obtém um ou mais recursos, executa as instruções especificadas e descarta automaticamente o objeto. No entanto, a using instrução é útil apenas para objetos que são usados dentro do escopo do método no qual eles são construídos.

O exemplo a seguir usa a using instrução para criar e liberar um System.IO.StreamReader objeto.

using System.IO;

class UsingStatement
{
    static void Main()
    {
        var buffer = new char[50];
        using (StreamReader streamReader = new("file1.txt"))
        {
            int charsRead = 0;
            while (streamReader.Peek() != -1)
            {
                charsRead = streamReader.Read(buffer, 0, buffer.Length);
                //
                // Process characters read.
                //
            }
        }
    }
}
Imports System.IO

Module UsingStatement
    Public Sub Main()
        Dim buffer(49) As Char
        Using streamReader As New StreamReader("File1.txt")
            Dim charsRead As Integer
            Do While streamReader.Peek() <> -1
                charsRead = streamReader.Read(buffer, 0, buffer.Length)
                ' 
                ' Process characters read.
                '
            Loop
        End Using
    End Sub
End Module

Uma using declaração é uma sintaxe alternativa disponível onde as chaves são removidas e o escopo está implícito.

using System.IO;

class UsingDeclaration
{
    static void Main()
    {
        var buffer = new char[50];
        using StreamReader streamReader = new("file1.txt");

        int charsRead = 0;
        while (streamReader.Peek() != -1)
        {
            charsRead = streamReader.Read(buffer, 0, buffer.Length);
            //
            // Process characters read.
            //
        }
    }
}

Embora a StreamReader classe implemente a IDisposable interface, o que indica que ela usa um recurso não gerenciado, o exemplo não chama explicitamente o StreamReader.Dispose método. Quando o compilador C# ou Visual Basic encontra a using instrução, ele emite linguagem intermediária (IL) que é equivalente ao código a seguir que contém explicitamente um try/finally bloco.

using System.IO;

class TryFinallyGenerated
{
    static void Main()
    {
        var buffer = new char[50];
        StreamReader? streamReader = null;
        try
        {
            streamReader = new StreamReader("file1.txt");
            int charsRead = 0;
            while (streamReader.Peek() != -1)
            {
                charsRead = streamReader.Read(buffer, 0, buffer.Length);
                //
                // Process characters read.
                //
            }
        }
        finally
        {
            // If non-null, call the object's Dispose method.
            streamReader?.Dispose();
        }
    }
}
Imports System.IO

Module TryFinallyGenerated
    Public Sub Main()
        Dim buffer(49) As Char
        Dim streamReader As New StreamReader("File1.txt")
        Try
            Dim charsRead As Integer
            Do While streamReader.Peek() <> -1
                charsRead = streamReader.Read(buffer, 0, buffer.Length)
                ' 
                ' Process characters read.
                '
            Loop
        Finally
            If streamReader IsNot Nothing Then DirectCast(streamReader, IDisposable).Dispose()
        End Try
    End Sub
End Module

A instrução C# using também permite que você adquira vários recursos em uma única instrução, que é internamente equivalente a instruções aninhadas using . O exemplo a seguir instancia dois StreamReader objetos para ler o conteúdo de dois arquivos diferentes.

using System.IO;

class SingleStatementMultiple
{
    static void Main()
    {
        var buffer1 = new char[50];
        var buffer2 = new char[50];

        using StreamReader version1 = new("file1.txt"),
                           version2 = new("file2.txt");

        int charsRead1, charsRead2 = 0;
        while (version1.Peek() != -1 && version2.Peek() != -1)
        {
            charsRead1 = version1.Read(buffer1, 0, buffer1.Length);
            charsRead2 = version2.Read(buffer2, 0, buffer2.Length);
            //
            // Process characters read.
            //
        }
    }
}

Tente/finalmente bloqueie

Em vez de envolver um try/finally bloco em uma using instrução, você pode optar por implementá-lo try/finally diretamente. Pode ser o seu estilo de codificação pessoal, ou você pode querer fazer isso por um dos seguintes motivos:

  • Para incluir um catch bloco para lidar com exceções lançadas try no bloco. Caso contrário, quaisquer exceções lançadas dentro da using instrução não serão tratadas.
  • Para instanciar um objeto que implementa IDisposable cujo escopo não é local para o bloco no qual ele é declarado.

O exemplo a seguir é semelhante ao exemplo anterior, exceto que ele usa um try/catch/finally bloco para instanciar, usar e descartar um StreamReader objeto e para manipular quaisquer exceções lançadas pelo StreamReader construtor e seu ReadToEnd método. O código no finally bloco verifica se o objeto que implementa IDisposable não null está antes de chamar o Dispose método. A falha em fazer isso pode resultar em uma NullReferenceException exceção em tempo de execução.

using System;
using System.Globalization;
using System.IO;

class TryExplicitCatchFinally
{
    static void Main()
    {
        StreamReader? streamReader = null;
        try
        {
            streamReader = new StreamReader("file1.txt");
            string contents = streamReader.ReadToEnd();
            var info = new StringInfo(contents);
            Console.WriteLine($"The file has {info.LengthInTextElements} text elements.");
        }
        catch (FileNotFoundException)
        {
            Console.WriteLine("The file cannot be found.");
        }
        catch (IOException)
        {
            Console.WriteLine("An I/O error has occurred.");
        }
        catch (OutOfMemoryException)
        {
            Console.WriteLine("There is insufficient memory to read the file.");
        }
        finally
        {
            streamReader?.Dispose();
        }
    }
}
Imports System.Globalization
Imports System.IO

Module TryExplicitCatchFinally
    Sub Main()
        Dim streamReader As StreamReader = Nothing
        Try
            streamReader = New StreamReader("file1.txt")
            Dim contents As String = streamReader.ReadToEnd()
            Dim info As StringInfo = New StringInfo(contents)
            Console.WriteLine($"The file has {info.LengthInTextElements} text elements.")
        Catch e As FileNotFoundException
            Console.WriteLine("The file cannot be found.")
        Catch e As IOException
            Console.WriteLine("An I/O error has occurred.")
        Catch e As OutOfMemoryException
            Console.WriteLine("There is insufficient memory to read the file.")
        Finally
            If streamReader IsNot Nothing Then streamReader.Dispose()
        End Try
    End Sub
End Module

Você pode seguir esse padrão básico se optar por implementar ou precisar implementar um try/finally bloco, porque sua linguagem de programação não suporta uma using instrução, mas permite chamadas diretas para o Dispose método.

Membros de instância IDisposable

Se uma classe possui um campo de instância ou propriedade e seu tipo implementa IDisposable, a classe também deve implementar IDisposable. Para obter mais informações, consulte Implementar um descarte em cascata.

Consulte também