Comece com essas classes simples ...
Digamos que eu tenha um conjunto simples de classes como este:
class Bus
{
Driver busDriver = new Driver();
}
class Driver
{
Shoe[] shoes = { new Shoe(), new Shoe() };
}
class Shoe
{
Shoelace lace = new Shoelace();
}
class Shoelace
{
bool tied = false;
}
A Bus
tem a Driver
, o Driver
tem dois Shoe
s, cada Shoe
um tem a Shoelace
. Tudo muito bobo.
Adicione um objeto IDisposable ao Shoelace
Mais tarde, decido que alguma operação no Shoelace
poderia ser multithread, então adiciono um EventWaitHandle
para os threads se comunicarem. Então Shoelace
agora fica assim:
class Shoelace
{
private AutoResetEvent waitHandle = new AutoResetEvent(false);
bool tied = false;
// ... other stuff ..
}
Implementar IDisposable no cadarço
Mas agora o FxCop da Microsoft irá reclamar: "Implemente IDisposable no 'Shoelace' porque cria membros dos seguintes tipos de IDisposable: 'EventWaitHandle'."
Ok, eu implementar IDisposable
em Shoelace
e minha classe pequeno puro torna-se essa bagunça horrível:
class Shoelace : IDisposable
{
private AutoResetEvent waitHandle = new AutoResetEvent(false);
bool tied = false;
private bool disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~Shoelace()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
if (waitHandle != null)
{
waitHandle.Close();
waitHandle = null;
}
}
// No unmanaged resources to release otherwise they'd go here.
}
disposed = true;
}
}
Ou (como apontado pelos comentaristas), como Shoelace
ele próprio não possui recursos não gerenciados, eu poderia usar a implementação de descarte mais simples sem precisar do Dispose(bool)
e Destructor:
class Shoelace : IDisposable
{
private AutoResetEvent waitHandle = new AutoResetEvent(false);
bool tied = false;
public void Dispose()
{
if (waitHandle != null)
{
waitHandle.Close();
waitHandle = null;
}
GC.SuppressFinalize(this);
}
}
Assista com horror como IDisposable se espalha
Certo, isso é fixo. Mas agora o FxCop irá reclamar que Shoe
cria um Shoelace
, assim também Shoe
deve ser IDisposable
.
E Driver
cria Shoe
assim Driver
deve ser IDisposable
. E Bus
cria Driver
assim Bus
deve ser IDisposable
e assim por diante.
De repente, minha pequena mudança Shoelace
está me causando muito trabalho e meu chefe está se perguntando por que preciso fazer o checkout Bus
para fazer uma alteração Shoelace
.
A questão
Como você evita essa propagação IDisposable
, mas ainda garante que seus objetos não gerenciados sejam descartados corretamente?