Ich bin kein Fan von Standardcode: Die Wiederverwendung durch Kopieren und Einfügen ist potenziell fehleranfällig. Selbst wenn Sie Codeschnipsel oder intelligente Vorlagen verwenden, gibt es keine Garantie dafür, dass der andere Entwickler dies auch getan hat, was bedeutet, dass es keine Garantie dafür gibt, dass er es richtig gemacht hat. Und wenn Sie den Code sehen müssen, müssen Sie ihn auch verstehen und/oder pflegen.
Was ich von der Gemeinschaft wissen möchte, ist: Ist meine Implementierung von IDispose für eine Klassenhierarchie eine legitime Alternative zur "traditionelles" Entsorgungsmuster ? Mit legitim meine ich korrekt, einigermaßen leistungsfähig, robust und wartbar.
Ich habe kein Problem damit, dass diese Alternative schlichtweg falsch ist, aber wenn sie es ist, würde ich gerne wissen, warum.
Bei dieser Implementierung wird davon ausgegangen, dass Sie die volle Kontrolle über die Klassenhierarchie haben; falls nicht, müssen Sie wahrscheinlich auf Boilerplate-Code zurückgreifen. Die Aufrufe von Hinzufügen*() wird normalerweise im Konstruktor vorgenommen.
public abstract class DisposableObject : IDisposable
{
protected DisposableObject()
{}
protected DisposableObject(Action managedDisposer)
{
AddDisposers(managedDisposer, null);
}
protected DisposableObject(Action managedDisposer, Action unmanagedDisposer)
{
AddDisposers(managedDisposer, unmanagedDisposer);
}
public bool IsDisposed
{
get { return disposeIndex == -1; }
}
public void CheckDisposed()
{
if (IsDisposed)
throw new ObjectDisposedException("This instance is disposed.");
}
protected void AddDisposers(Action managedDisposer, Action unmanagedDisposer)
{
managedDisposers.Add(managedDisposer);
unmanagedDisposers.Add(unmanagedDisposer);
disposeIndex++;
}
protected void AddManagedDisposer(Action managedDisposer)
{
AddDisposers(managedDisposer, null);
}
protected void AddUnmanagedDisposer(Action unmanagedDisposer)
{
AddDisposers(null, unmanagedDisposer);
}
public void Dispose()
{
if (disposeIndex != -1)
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
~DisposableObject()
{
if (disposeIndex != -1)
Dispose(false);
}
private void Dispose(bool disposing)
{
for (; disposeIndex != -1; --disposeIndex)
{
if (disposing)
if (managedDisposers[disposeIndex] != null)
managedDisposers[disposeIndex]();
if (unmanagedDisposers[disposeIndex] != null)
unmanagedDisposers[disposeIndex]();
}
}
private readonly IList<Action> managedDisposers = new List<Action>();
private readonly IList<Action> unmanagedDisposers = new List<Action>();
private int disposeIndex = -1;
}
Dies ist eine "vollständige" Implementierung in dem Sinne, dass ich die Finalisierung unterstütze (da ich weiß, dass die meisten Implementierungen keinen Finalizer benötigen), prüfe, ob ein Objekt entsorgt wurde, usw. Eine echte Implementierung könnte zum Beispiel den Finalizer entfernen oder eine Unterklasse von Einweg-Objekt die den Finalizer enthält. Im Grunde habe ich alles hineingeworfen, was mir zu dieser Frage einfiel.
Wahrscheinlich gibt es einige Sonderfälle und esoterische Situationen, die ich übersehen habe. Ich lade daher jeden ein, diesen Ansatz zu hinterfragen oder ihn zu korrigieren.
Andere Alternativen wären die Verwendung eines einzigen Queue<Disposer> Entsorger en Einweg-Objekt anstelle von zwei Listen; in diesem Fall werden die Entsorger, sobald sie aufgerufen werden, aus der Liste entfernt. Es gibt noch andere leichte Variationen, die ich mir vorstellen kann, aber sie haben das gleiche allgemeine Ergebnis: kein Kesselsteincode.
1 Stimmen
Dies leidet immer noch unter der Tatsache, dass Implementierer daran denken müssen, die Add()-Methode für aggregierte Typen aufzurufen. Ich persönlich glaube nicht, dass dies irgendeinen Vorteil gegenüber dem üblichen Muster hat.
5 Stimmen
Tun Sie es nicht. Verwenden Sie das Dispose-Muster wie geschrieben. Erfahrene Entwickler verstehen den Code auf einen Blick, Sie decken alle Grundlagen ab, Tools wie FxCop helfen bei der Validierung, Sie können Snippets schreiben, um ihn genau zu implementieren, und Sie halten sich an die Microsoft-Standards. bluebytesoftware.com/blog/
0 Stimmen
Nebenbei bemerkt, wenn Sie eine DisposableCollection<TDisposable> wollen, sollten Sie in der Lage sein, eine Reihe von Implementierungen davon zu finden.
0 Stimmen
Ok, mir ist gerade klar geworden, dass es mindestens ein Problem gibt. Wenn ein unmanagedDisposer auslöst, wird keiner der "Basis"-Disposer ausgeführt. Das passiert im traditionellen Muster nicht, weil die Basis-Finalizer immer ausgeführt werden.
0 Stimmen
Fast eine exakte Entsprechung dieser Frage: stackoverflow.com/questions/874764/
0 Stimmen
@spender: Diese Implementierung funktioniert, indem sie eine LISTE der zu entsorgenden Objekte führt. Für set-once-Felder hat ein solcher Ansatz meiner Meinung nach viel zu bieten, da er es ermöglicht, dass derselbe Code die IDisposable-Objekte erstellt und für deren Bereinigung sorgt. In vb.net, wenn Konstruktoren geschützt sind und nur öffentlich über Factory-Methoden verfügbar sind, kann man weiter gehen und sich um Deklaration, Initialisierung und Bereinigung kümmern, alles in der gleichen Codezeile, ein Muster, das IMHO viel schöner ist als die Deklaration an einer Stelle, die Initialisierung an einer anderen und die Bereinigung an einer dritten.