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 Bustem a Driver, o Drivertem dois Shoes, cada Shoeum tem a Shoelace. Tudo muito bobo.
Adicione um objeto IDisposable ao Shoelace
Mais tarde, decido que alguma operação no Shoelacepoderia ser multithread, então adiciono um EventWaitHandlepara os threads se comunicarem. Então Shoelaceagora 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 IDisposableem Shoelacee 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 Shoelaceele 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 Shoecria um Shoelace, assim também Shoedeve ser IDisposable.
E Drivercria Shoeassim Driverdeve ser IDisposable. E Buscria Driverassim Busdeve ser IDisposablee assim por diante.
De repente, minha pequena mudança Shoelaceestá me causando muito trabalho e meu chefe está se perguntando por que preciso fazer o checkout Buspara 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?