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.
Existe a forma equivalente de chamar o Dispose através do using.
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:
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:
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.
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:
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.
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.