IDisposable em C#: como não matar a performance da aplicação

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

IDisposable: a arma secreta da performance que as documentações não mencionam



Você já se perguntou por que sua aplicação C# começa a "ficar lenta" após algumas horas de execução? Ou por que o banco de dados de repente para de responder às consultas? Em 80% dos casos, o culpado é uma interface — ou melhor, seu uso incorreto. Estamos falando do IDisposable.



No Stack Overflow, a pergunta "Proper use of the IDisposable interface" acumulou milhares de visualizações e centenas de discussões. E não é por acaso: o gerenciamento incorreto de recursos é o maior inimigo da performance no .NET. Vamos entender como transformar o IDisposable de "magia negra" em uma ferramenta poderosa de otimização.



Por que IDisposable não é apenas uma "marcação" no código?



Muitos desenvolvedores veem o IDisposable como uma obrigação chata: "preciso implementar — implemento". Mas, na verdade, ele é seu maior aliado na luta pela performance. Imagine que sua aplicação é um escritório. O Garbage Collector (GC) é o faxineiro que vem à noite. E o IDisposable é seu assistente pessoal, que limpa a sujeira imediatamente, sem esperar pela manhã.



Sem o uso correto do IDisposable, você corre o risco de:

  • Vazamentos de memória (memory leaks)
  • Bloqueio de arquivos e conexões com banco de dados
  • Quedas inesperadas de performance
  • Problemas de escalabilidade


Fundamentos: o que é IDisposable e como funciona?



IDisposable é uma interface que contém apenas um método: Dispose(). Sua função é liberar recursos não gerenciados (arquivos, conexões de rede, handles de janelas) imediatamente, assim que não forem mais necessários.



public class ResourceHolder : IDisposable{    private IntPtr _unmanagedResource;    private 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 }

// Libera recursos não gerenciados if (_unmanagedResource != IntPtr.Zero) { Marshal.FreeHGlobal(_unmanagedResource); _unmanagedResource = IntPtr.Zero; }

_disposed = true; }

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


Observe o GC.SuppressFinalize(this) — este é um ponto-chave de otimização. Quando você chama Dispose explicitamente, você diz ao GC: "Não chame o finalizador, eu já fiz tudo". Isso acelera a coleta de lixo.



Padrão Dispose: evolução do simples ao complexo



Nível 1: Mais simples (apenas para recursos gerenciados)



Se sua classe usa apenas recursos gerenciados (outros objetos IDisposable), uma implementação simples é suficiente:



public class SimpleLogger : IDisposable{    private readonly FileStream _logFile;

public SimpleLogger(string path) { _logFile = new FileStream(path, FileMode.Append); }

public void Dispose() { _logFile?.Dispose(); }}


Nível 2: Padrão completo (com finalizador)



Para classes que trabalham com recursos não gerenciados (por exemplo, handles WinAPI), é necessário o padrão completo com finalizador:



public class SafeHandleWrapper : IDisposable{    private SafeHandle _handle;

public SafeHandleWrapper() { _handle = new SafeFileHandle(IntPtr.Zero, true); }

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

protected virtual void Dispose(bool disposing) { if (_handle != null && !_handle.IsInvalid) { _handle.Dispose(); } }

// O finalizador é necessário apenas se houve

Blogs

Book Recommendations