Wann sollte ich einen Destruktor erstellen?

185

Beispielsweise:

public class Person
{
    public Person()
    {
    }

    ~Person()
    {
    }
}

Wann sollte ich manuell einen Destruktor erstellen? Wann mussten Sie einen Destruktor erstellen?

David Heffernan
quelle
1
Die C # -Sprache nennt diese "Destruktoren", aber die meisten Leute nennen sie "Finalizer", da dies ihr .NET-Name ist und die Verwechslung mit C ++ - Destruktoren (die sehr unterschiedlich sind) verringert wird. So implementieren Sie IDisposable und Finalizer: 3 einfache Regeln
Stephen Cleary
5
Wenn Sie sich rücksichtslos fühlen.
Capdragon
32
Ich denke, TomToms Tastatur war fehlerhaft. Die Feststelltaste wurde sporadisch ein- und ausgeschaltet. Seltsam.
Jeff LaFay
siehe auch stackoverflow.com/questions/1076965/…
Ian Ringrose
Auf Vorschlag von Greg Beech habe ich einen Destruktor als Debugging-Hilfe verwendet: stackoverflow.com/questions/3832911/…
Brian

Antworten:

237

UPDATE: Diese Frage war das Thema meines Blogs im Mai 2015 . Danke für die tolle Frage! Im Blog finden Sie eine lange Liste von Unwahrheiten, die die Leute allgemein über die Finalisierung glauben.

Wann sollte ich manuell einen Destruktor erstellen?

Fast nie.

Normalerweise erstellt man einen Destruktor nur, wenn Ihre Klasse an einer teuren nicht verwalteten Ressource festhält, die bereinigt werden muss, wenn das Objekt verschwindet. Es ist besser, das Einwegmuster zu verwenden, um sicherzustellen, dass die Ressource bereinigt wird. Ein Destruktor ist dann im Wesentlichen eine Garantie dafür, dass die Ressource letztendlich immer noch bereinigt wird, wenn der Verbraucher Ihres Objekts vergisst, es zu entsorgen. (Vielleicht.)

Wenn Sie einen Destruktor erstellen, seien Sie äußerst vorsichtig und verstehen Sie, wie der Garbage Collector funktioniert . Zerstörer sind wirklich komisch :

  • Sie laufen nicht auf Ihrem Thread; Sie laufen auf ihrem eigenen Faden. Verursacht keine Deadlocks!
  • Eine unbehandelte Ausnahme, die von einem Destruktor ausgelöst wird, ist eine schlechte Nachricht. Es ist auf seinem eigenen Faden; Wer wird es fangen?
  • Ein Destruktor kann für ein Objekt aufgerufen werden, nachdem der Konstruktor gestartet wurde, aber bevor der Konstruktor beendet ist. Ein ordnungsgemäß geschriebener Destruktor stützt sich nicht auf im Konstruktor festgelegte Invarianten.
  • Ein Destruktor kann ein Objekt "wiederbeleben" und ein totes Objekt wieder lebendig machen. Das ist wirklich komisch. Tu es nicht.
  • Ein Destruktor könnte niemals rennen. Sie können sich nicht darauf verlassen, dass das Objekt jemals für die Finalisierung geplant wird. Es wird wahrscheinlich sein, aber das ist keine Garantie.

Fast nichts, was normalerweise wahr ist, ist in einem Destruktor wahr. Sei wirklich sehr, sehr vorsichtig. Das Schreiben eines korrekten Destruktors ist sehr schwierig.

Wann mussten Sie einen Destruktor erstellen?

Beim Testen des Teils des Compilers, der Destruktoren verarbeitet. Ich musste dies im Produktionscode nie tun. Ich schreibe selten Objekte, die nicht verwaltete Ressourcen manipulieren.

Eric Lippert
quelle
"Ein Destruktor kann für ein Objekt aufgerufen werden, nachdem der Konstruktor gestartet wurde, aber bevor der Konstruktor beendet ist." Aber ich kann mich darauf verlassen, dass Feldinitialisierer ausgeführt wurden, oder?
Konfigurator
13
@configurator: Nein. Angenommen, der dritte Feldinitialisierer eines Objekts mit einem Finalizer, der als statische Methode bezeichnet wird und eine Ausnahme auslöst. Wann würde der vierte Feldinitialisierer ausgeführt? Noch nie. Das Objekt ist jedoch noch zugeordnet und muss finalisiert werden. Sie haben nicht einmal die Garantie, dass Felder vom Typ double beim Ausführen des dtor vollständig initialisiert wurden. Nach der Hälfte des Schreibens des Double hätte ein Thread abgebrochen werden können, und jetzt muss sich der Finalizer mit einem halb initialisierten Half-Zero-Double befassen.
Eric Lippert
1
Ausgezeichneter Beitrag, sollte aber gesagt haben "sollte erstellt werden, wenn Ihre Klasse an einem teuren nicht verwalteten Objekt festhält oder eine große Anzahl nicht verwalteter Objekte vorhanden ist" - Für ein konkretes Beispiel habe ich eine Matrixklasse in C #, die ein zugrunde liegendes natives C ++ verwendet Matrix-Klasse für viel schweres Heben - ich mache viele Matrizen - ein "Destruktor" ist IDisposable in diesem speziellen Fall weit überlegen, weil er die verwalteten und nicht verwalteten Seiten des Hauses besser synchron hält
Mark Mullin
1
Pythonnet verwendet Destruktor, um GIL in nicht verwaltetem CPython
freizugeben
3
Super Artikel Eric. Requisiten dafür -> "Zusätzlicher Bonusspaß: Die Laufzeit verwendet weniger aggressive Codegenerierung und weniger aggressive Speicherbereinigung, wenn das Programm im Debugger ausgeführt wird, da es eine schlechte Debugging-Erfahrung ist, wenn Objekte, die Sie debuggen, plötzlich verschwinden, obwohl die Eine Variable, die sich auf das Objekt bezieht, befindet sich im Gültigkeitsbereich. Wenn Sie also einen Fehler haben, bei dem ein Objekt zu früh finalisiert wird, können Sie diesen Fehler wahrscheinlich nicht im Debugger reproduzieren! "
Ken Palmer
17

Es wird als "Finalizer" bezeichnet, und Sie sollten normalerweise nur einen für eine Klasse erstellen, deren Status (dh: Felder) nicht verwaltete Ressourcen enthält (dh: Zeiger auf Handles, die über p / invoke-Aufrufe abgerufen werden). In .NET 2.0 und höher gibt es jedoch einen besseren Weg, um mit der Bereinigung nicht verwalteter Ressourcen umzugehen : SafeHandle . Vor diesem Hintergrund sollten Sie so gut wie nie wieder einen Finalizer schreiben müssen.

Nicole Calinoiu
quelle
25
@ ThomasEding - Ja, das ist es . C # verwendet die Destruktorsyntax, erstellt jedoch tatsächlich einen Finalizer . Wieder .
JDB erinnert sich noch an Monica
@JDB: Das sprachliche Konstrukt wird als Destruktor bezeichnet. Ich mag den Namen nicht, aber so heißt er. Durch das Deklarieren eines Destruktors generiert der Compiler eine Finalizer-Methode, die ein wenig Wrapper-Code zusammen mit dem enthält, was im Hauptteil des Destruktors angezeigt wird.
Supercat
8

Sie benötigen keine, es sei denn, Ihre Klasse verwaltet nicht verwaltete Ressourcen wie Windows-Dateihandles.

John Saunders
quelle
5
Nun, eigentlich heißt es Destruktor
David Heffernan
2
Jetzt bin ich verwirrt. Ist es Finalizer oder Destruktor?
4
Die C # -Spezifikation nennt es tatsächlich einen Destruktor. Einige sehen dies als Fehler an. stackoverflow.com/questions/1872700/…
Ani
2
@ ThomasEding - Ja, das ist es . C # verwendet die Destruktorsyntax, erstellt jedoch tatsächlich einen Finalizer .
JDB erinnert sich noch an Monica
2
Ich liebe die Kommentare hier, echter Panto :)
Benjol
4

Es wird als Destruktor / Finalizer bezeichnet und normalerweise beim Implementieren des Disposed-Musters erstellt.

Es ist eine Fallback-Lösung, wenn der Benutzer Ihrer Klasse vergisst, Dispose aufzurufen, um sicherzustellen, dass (irgendwann) Ihre Ressourcen freigegeben werden, Sie jedoch keine Garantie dafür haben, wann der Destruktor aufgerufen wird.

In dieser Stapelüberlauffrage zeigt die akzeptierte Antwort korrekt, wie das Entsorgungsmuster implementiert wird. Dies ist nur erforderlich, wenn Ihre Klasse nicht gehandelte Ressourcen enthält, die der Garbage Collector nicht selbst bereinigen kann.

Es wird empfohlen, keinen Finalizer zu implementieren, ohne dem Benutzer der Klasse die Möglichkeit zu geben, das Objekt manuell zu entsorgen, um die Ressourcen sofort freizugeben.

Øyvind Bråthen
quelle
Tatsächlich wird es in C # aus gutem Grund NICHT als Destruktor bezeichnet.
TomTom
14
Eigentlich ist es so . Vielen Dank, dass Sie mir eine Ablehnung gegeben haben, weil Sie sich irren. Informationen
Øyvind Bråthen
1
@ TomTom sein offizieller Name ist Destruktor
David Heffernan
Es ist eigentlich keine Fallback-Methode, sondern ermöglicht dem GC lediglich die Verwaltung, wenn Ihre Objekte nicht verwaltete Ressourcen freigeben. Durch die Implementierung von IDisposable können Sie dies selbst verwalten.
HasaniH
3

Wenn Sie nicht verwaltete Ressourcen haben und sicherstellen müssen, dass diese bereinigt werden, wenn Ihr Objekt verschwindet. Ein gutes Beispiel wären COM-Objekte oder File Handler.

Vlad Bezden
quelle
2

Ich habe einen Destruktor (nur zu Debugzwecken) verwendet, um festzustellen, ob ein Objekt im Rahmen einer WPF-Anwendung aus dem Speicher gelöscht wurde. Ich war mir nicht sicher, ob die Speicherbereinigung das Objekt wirklich aus dem Speicher löscht, und dies war eine gute Möglichkeit, dies zu überprüfen.

Neil.Allen
quelle
1

Destruktoren bieten eine implizite Möglichkeit, nicht verwaltete Ressourcen in Ihrer Klasse freizugeben. Sie werden aufgerufen, wenn der GC dazu kommt, und sie rufen implizit die Finalize-Methode der Basisklasse auf. Wenn Sie viele nicht verwaltete Ressourcen verwenden, ist es besser, eine explizite Möglichkeit zum Freigeben dieser Ressourcen über die IDisposable-Schnittstelle bereitzustellen. Weitere Informationen finden Sie im C # -Programmierhandbuch: http://msdn.microsoft.com/en-us/library/66x5fx1b.aspx

HasaniH
quelle