Zacznij od tych prostych zajęć ...
Powiedzmy, że mam prosty zestaw klas, taki jak ten:
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 Busma Driver, Driverma dwa Shoes, każdy Shoema Shoelace. Bardzo głupie.
Dodaj obiekt IDisposable do Shoelace
Później zdecydowałem, że niektóre operacje na serwerze Shoelacemogą być wielowątkowe, więc dodaję EventWaitHandleznak, aby wątki się komunikowały. Więc Shoelaceteraz wygląda to tak:
class Shoelace
{
private AutoResetEvent waitHandle = new AutoResetEvent(false);
bool tied = false;
// ... other stuff ..
}
Zaimplementuj IDisposable na Shoelace
Ale teraz FxCop Microsoftu będzie narzekał: „Zaimplementuj IDisposable na 'Shoelace', ponieważ tworzy elementy członkowskie następujących typów IDisposable: 'EventWaitHandle'."
Dobra, wdrażam IDisposabledalej Shoelacei moja zgrabna mała klasa staje się strasznym bałaganem:
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;
}
}
Lub (jak podkreślają komentatorzy), ponieważ Shoelacesam nie ma niezarządzanych zasobów, mogę użyć prostszej implementacji usuwania bez potrzeby Dispose(bool)i 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);
}
}
Oglądaj z przerażeniem, jak rozprzestrzenia się IDisposable
Tak, to naprawione. Ale teraz FxCop będzie narzekać, że Shoetworzy plik Shoelace, więc też Shoemusi być IDisposable.
I Drivertworzy Shoetak Drivermusi być IDisposable. I Bustworzy Drivertak Busmusi być IDisposablei tak dalej.
Nagle moja mała zmiana w aplikacji Shoelacepowoduje dużo pracy, a mój szef zastanawia się, dlaczego muszę się wymeldować, Busaby dokonać zmiany Shoelace.
Pytanie
Jak zapobiec temu rozprzestrzenianiu się IDisposable, ale jednocześnie zapewnić, że niezarządzane obiekty są prawidłowo usuwane?