Skip to main content

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.