다음을 통해 공유


Exceptions: Sempre use try-finally

A interface IDisposable e try-finally tem uma relação muito próxima.

IDisposable é uma interface que implementa o método Dispose, que realiza a limpeza dos recursos de forma determinística. Normalmente esse método é chamado dentro de um bloco finally.

image

Existe a forma equivalente de chamar o Dispose através do using.

image

A regra é simples: se o objeto implementa a interface IDisposable e possui o método Dispose, então use try-finally ou using. Alguns exemplos importantes de objetos que devem ser limpos:

  • Banco de dados: SqlConnection e DataReader
  • Arquivos: StreamWriter, StreamReader, FileStream

No último artigo, mostrei um exemplo de um erro muito comum: “The timeout period elapsed prior to obtaining a connection from the pool”. Esse erro ocorre porque as conexões não são fechadas corretamente e ficam aguardando para serem finalizadas pela Finalizer thread.

A solução apresentada foi:

image

No entanto, o código não está 100% correto. Note que o objeto SqlCommand também implementa a interface IDisposable e, portanto, a solução ideal seria:

image

Embora o SqlCommand use somente recursos gerenciados, essa escrita tem a vantagem de chamar o GC.SupressFinalize contra o objeto, evitando adicionar o objeto à fila de finalização.

Implementando o IDisposable

Existem casos que precisamos implementar a interface IDisposable na nossa classe. Por exemplo, quando alocamos um objeto Disposable e armazenamos em uma variável membro.

image

O código acima está incorreto. Ninguém chama o método Dispose do SqlConnection, portanto, a conexão fica aberta e será fechada pela Finalizer Thread em um momento posterior (normalmente logo depois de um Garbage Collection).

O correto é implementar a interface IDisposable para permitir a limpeza de recursos:

image

Encontrei uma discussão muito boa sobre o IDisposable no Stack Overflow.

Proper use of the IDisposable interface
https://stackoverflow.com/questions/538060/proper-use-of-the-idisposable-interface

Vou aproveitar um dos exemplos (reescrevi o código) para mostrar uma situação que NÃO devemos usar o IDisposable. A pergunta inicial é se a classe MyCollection deve implementar o método Dispose para agilizar a limpeza de memória. A resposta é NÃO para memória gerenciada.

image

O objetivo do método Dispose é realizar a limpeza determinística de recursos não-gerenciados e objetos IDisposable. No exemplo anterior, a classe MyCollection possui a variável membro do tipo List<String>, que não é IDisposable.

Conclusão

Nesse artigo comentei sobre a interface IDisposable e sua relação próxima com o try-finally. Infelizmente esse é um tema que ninguém presta atenção. Já vi pessoas defendendo o uso do GC.Collect no código para garantir performance! Não tenho dúvida de que esquecer de chamar o método Dispose é um dos erros mais comuns de programação C#.

A regra é sempre usar try-finally ou using nos objetos Disposable.