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 Bus
ma Driver
, Driver
ma dwa Shoe
s, każdy Shoe
ma Shoelace
. Bardzo głupie.
Dodaj obiekt IDisposable do Shoelace
Później zdecydowałem, że niektóre operacje na serwerze Shoelace
mogą być wielowątkowe, więc dodaję EventWaitHandle
znak, aby wątki się komunikowały. Więc Shoelace
teraz 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 IDisposable
dalej Shoelace
i 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ż Shoelace
sam 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 Shoe
tworzy plik Shoelace
, więc też Shoe
musi być IDisposable
.
I Driver
tworzy Shoe
tak Driver
musi być IDisposable
. I Bus
tworzy Driver
tak Bus
musi być IDisposable
i tak dalej.
Nagle moja mała zmiana w aplikacji Shoelace
powoduje dużo pracy, a mój szef zastanawia się, dlaczego muszę się wymeldować, Bus
aby dokonać zmiany Shoelace
.
Pytanie
Jak zapobiec temu rozprzestrzenianiu się IDisposable
, ale jednocześnie zapewnić, że niezarządzane obiekty są prawidłowo usuwane?