Inizia con queste semplici classi ...
Diciamo che ho un semplice set di classi come questo:
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 Busha a Driver, Driverha due Shoes, ognuna Shoeha a Shoelace. Tutto molto sciocco.
Aggiungi un oggetto IDisposable a Laccio
In seguito decido che alcune operazioni su Shoelacepotrebbero essere multi-thread, quindi aggiungo un messaggio EventWaitHandleper comunicare con i thread. Quindi Shoelaceora appare così:
class Shoelace
{
private AutoResetEvent waitHandle = new AutoResetEvent(false);
bool tied = false;
// ... other stuff ..
}
Implementare IDisposable su Shoelace
Ma ora FxCop di Microsoft si lamenterà: "Implementa IDisposable su 'Shoelace' perché crea membri dei seguenti tipi IDisposable: 'EventWaitHandle'."
Va bene, implemento IDisposablesu Shoelacee la mia classe di poco pulito diviene questa orribile pasticcio:
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;
}
}
Oppure (come sottolineato dai commentatori) poiché di per Shoelacesé non ha risorse non gestite, potrei usare l'implementazione di smaltimento più semplice senza la necessità di 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);
}
}
Guarda con orrore gli spread IDisposable
Esatto, è quello risolto. Ma ora FxCop si lamenterà che Shoecrea un Shoelace, così Shoedeve essere IDisposableanche.
E Drivercrea Shoecosì Driverdeve essere IDisposable. E Buscrea Drivercosì Busdeve essere IDisposablee così via.
Improvvisamente la mia piccola modifica Shoelacemi sta causando molto lavoro e il mio capo si chiede perché devo fare il checkout Busper fare una modifica Shoelace.
La domanda
Come evitare questa diffusione di IDisposable, ma garantire comunque che gli oggetti non gestiti siano correttamente smaltiti?