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 Bus
ha a Driver
, Driver
ha due Shoe
s, ognuna Shoe
ha a Shoelace
. Tutto molto sciocco.
Aggiungi un oggetto IDisposable a Laccio
In seguito decido che alcune operazioni su Shoelace
potrebbero essere multi-thread, quindi aggiungo un messaggio EventWaitHandle
per comunicare con i thread. Quindi Shoelace
ora 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 IDisposable
su Shoelace
e 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 Shoelace
sé 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 Shoe
crea un Shoelace
, così Shoe
deve essere IDisposable
anche.
E Driver
crea Shoe
così Driver
deve essere IDisposable
. E Bus
crea Driver
così Bus
deve essere IDisposable
e così via.
Improvvisamente la mia piccola modifica Shoelace
mi sta causando molto lavoro e il mio capo si chiede perché devo fare il checkout Bus
per fare una modifica Shoelace
.
La domanda
Come evitare questa diffusione di IDisposable
, ma garantire comunque che gli oggetti non gestiti siano correttamente smaltiti?