Liberação de Recursos com Dispose Pattern

É bastante comum nos projetos de hoje em dia encontrar uma situação na qual trabalhamos com diversos recursos na memória. Existem recursos gerenciáveis e não gerenciáveis pela máquina virtual do .Net, a Common Language Runtime (CLR). Após a utilização, devemos liberar a memória ocupada por esses objetos. Quando um objeto gerenciado não está mais sendo utilizado, o garbage collector libera automaticamente a memória alocada para ele, mas não é possível prever quando ocorrerá a coleta. Já os recursos não gerenciáveis, tais como conexões com banco de dados, arquivos e hardwares externos, têm de ser liberados manualmente.

E como implementar?

Você implementa a interface IDisposable para fazer a liberação correta do recurso de modo que não haja risco de algum usuário desta classe acessar um recurso que já foi liberado (para não ser lançada a exceção ObjectDisposedException).

É aconselhável chamar o método Dispose apenas no lugar onde você deve limpar o recurso.

Seguem abaixo as abordagens possíveis de liberação de recursos:

  • try-finally

É o jeito mais antigo de consumir recursos desde que “using” foi implementado no .NET Framework. Uma regra básica ao se consumir um recurso é garantir o sucesso de quem for utilizá-lo. É claro que algum erro pode acontecer nessa operação, isso explica o motivo de colocar as instruções dentro de um bloco try. No bloco finally vai a chamada explícita ao método Dispose. É seguro pois o bloco finally sempre será executado. Então, a liberação de recursos ocorre no bloco finally.

/// <summary>
/// Cria um novo arquivo em um diretório especificado. Se o arquivo já existe,
/// modifica-se seu nome e tenta criá-lo novamente.
/// </summary>
public void ExecResource()
 {
     FileStream fs = null;
     string path = @"C:\Users\Thiago\Dropbox\Teste.txt";
     try
     {
         fs = new FileStream(path, FileMode.CreateNew, FileAccess.ReadWrite);
     }
     catch (IOException)
     {
         if (File.Exists(path))
         {
             var newPath = path.Replace("Teste", "outro");
             //tenta criar o arquivo com novo path
             fs = new FileStream(newPath, FileMode.CreateNew, FileAccess.ReadWrite);
         }
     }
     finally
     {
         if (fs != null)
            fs.Dispose();
     }
 }

  •  using

A diferença dessa abordagem é pelo fato desse bloco fazer uma chamada implícita ao método Dispose ao final de seu escopo. É necessário para a classe que utiliza o using implementar a interface IDisposable. É recomendável, por ser um recurso da linguagem, pois dispensa uma limpeza manual. O compilador gera automaticamente os blocos try-finally. Você instancia o recurso dentro da declaração do using:


 /// <summary>
 /// Carrega um arquivo texto e copia-o em um novo arquivo
 /// </summary>
 /// <param name="path"></param>
 public void ExecResource(string path)
 {
     string destPath = @"C:\Users\Thiago\Dropbox\Teste2.txt";

     using (TextReader txt = new StreamReader(path))
     {
         if (File.Exists(path))
         {
             File.Copy(path, destPath);
             Console.WriteLine("Arquivo copiado com sucesso.");
         }
     }
 }

Obs: a maioria das classes do .NET Framework, como StreamReader/StreamWriter, DataTable, dentre outros, já implementa o método Dispose pela interface IDisposable. Nesse caso, portanto, atenha-se apenas ao uso do bloco using.

  •  Atenção: Finalizers (destrutores de objetos)

Os métodos finalizers são chamados no final do ciclo de varredura do Garbage Collector. Eles realizam a limpeza de recursos não gerenciados também. É uma alternativa usada quando o objeto que consome o recurso não chama o Dispose. Note que não é necessário implementar um finalizer se você chamou o método Dispose no seu bloco finally ou se usou using implementando a interface IDisposable. Um exemplo disso é a classe BinaryWriter. Alguns autores como Jeffrey Richter (CLR via C#, 2010) gostam de implementar um finalizer junto com o método Dispose para ter mais controle sobre o ciclo de vida do recurso.

 Implementando o Dispose Pattern

1) Primeiro, crie uma classe que vai implementar a interface IDisposable;

2) Crie um flag que vai controlar se o recurso já foi liberado ou não (bool disposed);

3) Crie um método Dispose recebendo um parâmetro (boolean disposing) que, dependendo do valor, se for true vai liberar os recursos gerenciados e false, os não gerenciados. Este método deve ser declarado como protected virtual por causa da hierarquia de herança. Se você quiser definir classes derivadas que vão precisar liberar recursos não gerenciados após seu uso, na subclasse você deve sobrescrevê-lo. É assinado com protected para limitar sua visibilidade por outras classes fora da hierarquia. Obs: a classe base (caso haja) não deve possuir nenhum finalizer.

4) Crie um método Dispose sem parâmetro. Ele chamará o método Dispose(true) que de fato realizará as operações de liberação de recursos gerenciados e também fará uma chamada ao método SuppressFinalize do Garbage Collector, impedindo que ele chame o finalizer para esse objeto.

5) Por fim, utilize o finalizer apenas se você não tiver implementado seu código dentro de um bloco using ou try-finally com uma chamada ao Dispose.


class ResourceExample : IDisposable
{
    bool disposed = false;

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposed)
          return;

        if (disposing)
        {
          //libera recursos gerenciados pela CLR
        }

        //else: libera recursos não gerenciados pela CLR

        disposed = true;
    }

    //se quiser sobrescrever (override) o finalizer, passe false no Dispose(disposing)

    ~ResourceExample()
    {
        Dispose(false);
    }
}

Enfim, esse assunto é bem mais extenso e se você quiser se aprofundar em Dispose Pattern aqui vão dois links:

MSDN

CLR via C# – Cap. 21 – Automatic Memory Management (Garbage Collection)

Até a próxima,

Thiago

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s