IDisposable | Dispose | CSharp
- IDisposable | Dispose | CSharp
1. O que é IDisposable
IDisposable é uma interface de contrato explícito que indica que um objeto:
- Controla recursos externos ao GC
- Precisa ser finalizado manualmente
- Não pode depender apenas do Garbage Collector
public interface IDisposable
{
void Dispose();
}
2. O ponto mais importante (corrigindo o texto)
❗ O
Dispose()NÃO é chamado automaticamente pelo GC
Isso é 100% correto.
O GC:
-
Coleta memória gerenciada
-
NÃO sabe:
- quando fechar socket
- quando fechar arquivo
- quando fechar conexão
-
NÃO chama
Dispose()
📌 Dispose() é sempre uma decisão explícita.
3. Então quem chama o Dispose()?
Existem apenas 3 formas corretas:
3.1 using (forma mais comum)
using var stream = new FileStream("file.txt", FileMode.Open);
// Dispose() é chamado automaticamente ao sair do escopo
Equivalente a:
var stream = new FileStream(...);
try
{
...
}
finally
{
stream.Dispose();
}
3.2 Container de DI (ASP.NET / Worker / Host)
Quando um objeto:
- é criado pelo container
- tem ciclo de vida controlado (
Scoped,Singleton,Transient) - implementa
IDisposable
👉 O container chama Dispose() automaticamente no final do ciclo de vida.
Exemplo:
services.AddSingleton<MyService>(); // MyService : IDisposable
No shutdown da aplicação:
Dispose() é chamado automaticamente
📌 Isso não é GC, é o host do .NET.
3.3 Chamada manual
var conn = new RabbitMqConnectionManager(...);
// ...
conn.Dispose();
Usado quando:
- O objeto não é gerenciado por DI
- Você controla o ciclo de vida manualmente
4. Quando usar IDisposable
Use IDisposable quando a classe controla:
- Sockets
- Arquivos
- Conexões de banco
- Conexões RabbitMQ
- Streams
- Handles nativos
- Recursos externos
Regra prática:
Se o objeto “fala com o mundo externo”, ele provavelmente é
IDisposable.
5. Quando NÃO usar IDisposable
❌ Não use para:
- Classes de domínio
- DTOs
- Serviços puros
- Regras de negócio
- Objetos só com memória gerenciada
Exemplo errado:
public class Pedido : IDisposable // ❌
{
public void Dispose() { }
}
Isso é ruído arquitetural.
6. IDisposable ≠ Finalizer (~ClassName())
Finalizer
~MyClass()
{
// cleanup nativo
}
- Executado pelo GC
- Tempo não determinístico
- Pode nunca rodar antes do processo morrer
- Mais lento
Dispose()
- Determinístico
- Chamado explicitamente
- Forma correta de liberar recursos
📌 Finalizer não substitui Dispose.
7. O padrão correto de implementação
Forma simples (mais comum)
public sealed class ResourceHolder : IDisposable
{
private readonly IConnection _connection;
public void Dispose()
{
_connection?.Dispose();
}
}
Forma completa (quando há herança ou recursos nativos)
public class ResourceHolder : IDisposable
{
private bool _disposed;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
// liberar recursos gerenciados
}
// liberar recursos não gerenciados
_disposed = true;
}
~ResourceHolder()
{
Dispose(false);
}
}
⚠️ Só use isso quando necessário. Para 90% dos casos, é overkill.
8. Relação entre Dispose() e exceções
-
Dispose()NÃO deve lançar exceções -
Ele é chamado em
finally -
Exceção aqui:
- quebra fluxo
- causa leaks
- dificulta shutdown
9. Relação com RabbitMQ (exemplo prático)
public sealed class RabbitMQConnectionManager : IDisposable
{
private readonly IConnection _connection;
public void Dispose()
{
if (_connection.IsOpen)
_connection.Close();
_connection.Dispose();
}
}
📌 Aqui:
-
O
Dispose()é chamado:- no shutdown da API
- pelo container de DI
-
Não depende do GC
-
Fecha socket TCP corretamente
Frase final (pra fechar)
Dispose()não é automático. É responsabilidade de quem cria ou gerencia o objeto. O GC cuida da memória. Você cuida dos recursos.
Essa é uma das distinções mais importantes do C# moderno.