Ist es in Ordnung, eine virtuelle Methode aus Entsorgen oder destructor anrufen?

StackOverflow https://stackoverflow.com/questions/137621

  •  02-07-2019
  •  | 
  •  

Frage

Ich kann keinen Hinweis darauf finden, aber ich erinnere mich, dass es keine gute Idee war, in einem destructor oder der Dispose () -Methode von IDisposable virtuellen (polymorphen) Methoden aufrufen.

Ist das wahr und wenn ja, kann mir jemand erklären, warum?

War es hilfreich?

Lösung

Beim virtuellen Methoden von einem Finalizerthread / Dispose ist unsicher, aus den gleichen Gründen es ist unsicher in einem Konstruktor zu tun. Es ist unmöglich, sicher zu sein, dass die abgeleitete Klasse einig Zustand nicht bereits aufgeräumte hat, dass die virtuelle Methode ordnungsgemäß ausgeführt werden muss.

Einige Leute werden durch den Standard Einweg Muster verwirrt, und seine Verwendung einer virtuellen Methode, virtual Dispose(bool disposing), und denke, das macht es Ok zu verwenden jeder virtuelle Methode ein dispose Durring. Betrachten Sie den folgenden Code ein:

class C : IDisposable {
    private IDisposable.Dispose() {
        this.Dispose(true);
    }
    protected virtual Dispose(bool disposing) {
        this.DoSomething();
    }

    protected virtual void DoSomething() {  }
}
class D : C {
    IDisposable X;

    protected override Dispose(bool disposing) {
        X.Dispose();
        base.Dispose(disposing);
    }

    protected override void DoSomething() {
        X.Whatever();
    }
}

Hier ist, was passiert, wenn Sie vom Typ D Entsorgen und Objekt, genannt d:

  1. Einige Code ruft ((IDisposable)d).Dispose()
  2. C.IDisposable.Dispose() ruft die virtuelle Methode D.Dispose(bool)
  3. D.Dispose(bool) verfügt über D.X
  4. D.Dispose(bool) ruft C.Dispose(bool) statisch (das Ziel des Aufrufs ist bekannt, zur Compile-Zeit )
  5. C.Dispose(bool) ruft die virtuellen Methoden D.DoSomething()
  6. D.DoSomething ruft die Methode D.X.Whatever() auf der bereits angeordneten D.X
  7. ?

Nun, die meisten Menschen, die diesen Code ausführen tun eine Sache, es zu beheben - sie den base.Dispose(dispose) Aufruf, bevor sie Bereinigungs ihr eigenes Objekt zu bewegen. Und, ja, das funktioniert. Aber vertrauen Sie wirklich Programmierer X, mit dem Ultra-Junior Entwickler von der Firma, die Sie C für zugewiesen entwickelt, D zu schreiben, es in einer Art und Weise zu schreiben, dass der Fehler entweder erkannt oder den base.Dispose(disposing) Anruf an der richtigen Stelle?

ich Ihnen nicht sagen, sollte nie, immer Code schreiben, die eine virtuelle Methode von Dispose ruft, nur, dass Sie auf benötigen Dokument , dass virtuelle Methode des Anforderung , dass es nie einen Zustand verwenden, die unter C.

Andere Tipps

sind virtuelle Methoden in beiden Konstruktoren und Destruktoren abgeraten.

Der Grund ist praktischer als alles andere: virtuelle Methoden können in irgendeiner Art und Weise durch die Übergehungseinrichtung gewählt außer Kraft gesetzt werden, und Dinge wie Objektinitialisierung während der Bauphase, zum Beispiel, haben sichergestellt werden, , damit Sie am Ende mit einem Objekt, das zufällige Nullen und einen ungültigen Zustand hat.

Ich glaube nicht, gibt es eine Empfehlung gegen virtuelle Methoden aufrufen. Das Verbot Sie erinnern sich vielleicht die Regel gegen Referenzierung verwalteten Objekte in der Finalizerthread.

Es ist ein Standardmuster, die .NET-Dokumentation definiert wird, wie Dispose () implementiert werden soll. Das Muster ist sehr gut gestaltet, und es sollte genau befolgt werden.

Der Kern ist dies: Entsorgung () ist eine nicht-virtuelle Methode, die eine virtuelle Methode Dispose (bool) aufruft. Der boolesche Parameter zeigt an, ob das Verfahren von Entsorgen aufgerufen wird () (true) oder das Objekt destructor (false). Auf jeder Ebene der Vererbung sollte die Dispose (bool) -Methode jede Bereinigung zu handhaben implementiert werden.

Wenn Dispose (bool) der Wert false übergeben wird, bedeutet dies, dass die Finalizerthread der dispose-Methode aufgerufen hat. Unter diesen Umständen sollte nur Bereinigung von nicht verwalteten Objekten versucht wird (außer in bestimmten seltenen Fällen). Der Grund dafür ist die Tatsache, dass der Garbage Collector hat gerade die Finalisierung Methode aufgerufen, damit das aktuelle Objekt ready-for-Abschluss markiert worden sein. Daher kann jedes Objekt, das es verweist auch schreib für Transaktionsabschluss wurde möglicherweise markiert, und da die Sequenz in nicht-deterministisch, kann die Fertigstellung bereits aufgetreten ist.

Ich empfehle Sie die Dispose () Muster in der .NET-Dokumentation nachschlagen und es genau folgen, weil es Ihnen wahrscheinlich aus bizarr und schwer Bugs schützen!

auf Jons Antwort zu erweitern, statt virtuelle Methoden des Aufrufs sollten Sie die dispose oder den destructor an Unterklassen werden überschreiben, wenn Sie Ressourcen auf dieser Ebene behandeln müssen.

Obwohl, ich glaube nicht, dass es eine „Regel“ in Bezug auf das Verhalten hier. Aber der allgemeine Gedanke ist, dass Sie wollen nur auf dieser Ebene der Umsetzung dieser Instanz Ressource Bereinigung zu isolieren.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top