Andere haben bereits den Unterschied zwischen Disposeund behandelt Finalize(übrigens wird die FinalizeMethode in der Sprachspezifikation immer noch als Destruktor bezeichnet), daher möchte ich nur ein wenig zu den Szenarien hinzufügen, in denen die FinalizeMethode nützlich ist.
Einige Typen kapseln verfügbare Ressourcen so, dass sie einfach zu verwenden sind, und entsorgen sie in einer einzigen Aktion. Die allgemeine Verwendung ist oft wie folgt: Öffnen, Lesen oder Schreiben, Schließen (Entsorgen). Es passt sehr gut zum usingKonstrukt.
Andere sind etwas schwieriger. WaitEventHandlesZum Beispiel werden Instanzen nicht so verwendet, da sie verwendet werden, um von einem Thread zu einem anderen zu signalisieren. Es stellt sich dann die Frage, wer diese anrufen soll Dispose. Als Schutz implementieren solche Typen eine FinalizeMethode, die sicherstellt, dass Ressourcen verfügbar sind, wenn die Instanz nicht mehr von der Anwendung referenziert wird.
Ich konnte diese genehmigte Antwort nicht verstehen. Ich möchte immer noch das andere wissen. Was es ist?
Ismael
22
@Ismael: Die größte Situation, Finalizedie gerechtfertigt sein kann, ist, wenn es eine Reihe von Objekten gibt, die daran interessiert sind, eine Ressource am Leben zu erhalten, aber es gibt keine Möglichkeit, mit der ein Objekt, das nicht mehr an der Ressource interessiert ist, herausfinden kann, ob es die ist Letzter. In diesem Fall Finalizewird normalerweise nur ausgelöst, wenn sich niemand für das Objekt interessiert. Das lose Timing von Finalizeist für nicht fungible Ressourcen wie Dateien und Sperren schrecklich, für fungible Ressourcen jedoch möglicherweise in Ordnung.
Supercat
13
+1 an Supercat für ein tolles neues (für mich) Wort. Der Kontext machte es ziemlich klar, aber nur für den Rest von uns, hier ist, was Wikipedia sagt: "Fungibilität ist das Eigentum eines Gutes oder einer Ware, deren einzelne Einheiten zur gegenseitigen Substitution fähig sind, wie süßes Rohöl ein Unternehmen, Anleihen, Edelmetalle oder Währungen. "
Jon Coombs
5
@ JonCoombs: Das ist ziemlich richtig, obwohl es erwähnenswert sein kann, dass der Begriff "fungible Ressource" auf Dinge angewendet wird, die frei substituierbar sind, bis sie erworben werden und nach Freigabe oder Aufgabe wieder frei substituierbar werden . Wenn das System über einen Pool von Sperrobjekten verfügt und Code eines erwirbt, das es einer Entität zuordnet , kann diese Sperre möglicherweise nicht durch diese Sperre ersetzt werden , solange jemand einen Verweis auf diese Sperre zum Zwecke der Zuordnung zu dieser Entität hält jede andere. Wenn jedoch der gesamte Code, der sich um die geschützte Entität kümmert, die Sperre aufgibt, ...
Supercat
... dann würde es wieder frei substituierbar werden, bis es mit einer anderen Entität verbunden ist.
Supercat
135
Die Finalizer-Methode wird aufgerufen, wenn Ihr Objekt durch Müll gesammelt wird und Sie keine Garantie haben, wann dies geschehen wird (Sie können es erzwingen, aber es beeinträchtigt die Leistung).
Die DisposeMethode hingegen soll von dem Code aufgerufen werden, der Ihre Klasse erstellt hat, damit Sie alle Ressourcen, die Sie erworben haben (nicht verwaltete Daten, Datenbankverbindungen, Dateihandles usw.), bereinigen und freigeben können, sobald der Code fertig ist Ihr Objekt.
Die Standardpraxis besteht darin, zu implementieren IDisposableund DisposeIhr Objekt in einer usingAnweisung zu verwenden. Wie zum Beispiel using(var foo = new MyObject()) { }. Und in Ihrem Finalizer rufen Sie an Dispose, nur für den Fall, dass der aufrufende Code vergessen hat, über Sie zu verfügen.
Sie müssen etwas vorsichtig sein, wenn Sie Dispose aus Ihrer Finalize-Implementierung aufrufen. Dispose kann auch verwaltete Ressourcen entsorgen, die Sie von Ihrem Finalizer nicht berühren möchten, da diese möglicherweise bereits selbst finalisiert wurden.
Itowlson
6
@itowlson: Die Überprüfung auf Null in Kombination mit der Annahme, dass Objekte zweimal entsorgt werden können (wobei der zweite Aufruf nichts bewirkt), sollte ausreichend sein.
Samuel
7
Das Standard-IDisposal-Muster und die versteckte Implementierung eines Dispose (Bool) zur optionalen Entsorgung verwalteter Komponenten scheinen dieses Problem zu lösen.
Brody
Es hört sich so an, als gäbe es keinen Grund, den Destruktor (die ~ MyClass () -Methode) zu implementieren und stattdessen immer die Dispose () -Methode zu implementieren und aufzurufen. Oder liege ich falsch? Könnte mir jemand ein Beispiel geben, wenn beide implementiert werden sollten?
dpelisek
66
Finalize ist die Backstop-Methode, die vom Garbage Collector aufgerufen wird, wenn ein Objekt zurückgefordert wird. Dispose ist die "deterministische Bereinigungsmethode", die von Anwendungen aufgerufen wird, um wertvolle native Ressourcen (Fensterhandles, Datenbankverbindungen usw.) freizugeben, wenn sie nicht mehr benötigt werden, anstatt sie unbegrenzt zu belassen, bis der GC das Objekt erreicht.
Als Benutzer eines Objekts verwenden Sie immer Dispose. Finalisieren ist für den GC.
Wenn Sie als Implementierer einer Klasse verwaltete Ressourcen besitzen, die entsorgt werden sollen, implementieren Sie Dispose. Wenn Sie native Ressourcen besitzen, implementieren Sie sowohl Dispose als auch Finalize und rufen beide eine gemeinsame Methode auf, mit der die nativen Ressourcen freigegeben werden. Diese Redewendungen werden normalerweise durch eine private Dispose-Methode (Bool Disposing) kombiniert, bei der Aufrufe mit true entsorgt und Aufrufe mit false abgeschlossen werden. Diese Methode gibt immer native Ressourcen frei, überprüft dann den Dispositionsparameter. Wenn dies zutrifft, werden verwaltete Ressourcen entsorgt und GC.SuppressFinalize aufgerufen.
Das ursprünglich empfohlene Muster für Klassen, die eine Mischung aus selbstreinigenden ("verwalteten") und nicht selbstreinigenden ("nicht verwalteten") Ressourcen enthielten, ist seit langem veraltet. Ein besseres Muster besteht darin, jede nicht verwaltete Ressource separat in ein eigenes verwaltetes Objekt zu verpacken, das keine starken Verweise auf etwas enthält, das für die Bereinigung nicht erforderlich ist. Für alles, auf das ein finalisierbares Objekt eine direkte oder indirekte starke Referenz enthält, wird die GC-Lebensdauer verlängert. Wenn Sie die Dinge zusammenfassen, die für die Bereinigung benötigt werden, können Sie vermeiden, die GC-Lebensdauer von Dingen zu verlängern, die dies nicht sind.
Supercat
2
@JCoombs: Disposeist gut und die korrekte Implementierung ist im Allgemeinen einfach. Finalizeist böse und es richtig zu implementieren ist im Allgemeinen schwierig. Da der GC sicherstellt, dass die Identität eines Objekts niemals "recycelt" wird, solange ein Verweis auf dieses Objekt vorhanden ist, ist es unter anderem einfach, eine Reihe von DisposableObjekten zu bereinigen, von denen einige möglicherweise bereits bereinigt wurden kein Problem; Jede Referenz auf ein Objekt, auf das Disposebereits aufgerufen wurde, bleibt eine Referenz auf ein Objekt, auf das Disposebereits aufgerufen wurde.
Supercat
2
@JCoombs: Nicht verwaltete Ressourcen haben im Allgemeinen keine solche Garantie. Wenn das Objekt Freddas Dateihandle Nr. 42 besitzt und es schließt, hängt das System möglicherweise dieselbe Nummer an das Dateihandle an, das einer anderen Entität zugewiesen wird. In diesem Fall bezieht sich das Dateihandle Nr. 42 nicht auf Freds geschlossene Datei, sondern auf die Datei, die von dieser anderen Entität aktiv verwendet wurde. denn Fredzu versuchen, Griff Nr. 42 wieder zu schließen, wäre katastrophal. Der Versuch, zu 100% zuverlässig zu verfolgen, ob ein nicht verwaltetes Objekt noch freigegeben wurde, ist praktikabel. Der Versuch, mehrere Objekte im Auge zu behalten, ist viel schwieriger.
Supercat
2
@JCoombs: Wenn jede nicht verwaltete Ressource in einem eigenen Wrapper-Objekt platziert ist, das nur ihre Lebensdauer steuert, dann außerhalb des Codes, der nicht weiß, ob die Ressource freigegeben wurde, aber weiß, dass dies der Fall sein sollte, wenn dies noch nicht geschehen ist kann das Wrapper-Objekt sicher auffordern, es freizugeben; Das Wrapper-Objekt weiß, ob es dies getan hat, und kann die Anforderung ausführen oder ignorieren. Die Tatsache, dass der GC garantiert, dass ein Verweis auf den Wrapper immer ein gültiger Verweis auf den Wrapper ist, ist eine sehr nützliche Garantie.
Supercat
43
Finalisieren
Finalizer sollten immer sein protected, nicht publicoder privateso, dass die Methode nicht direkt aus dem Code der Anwendung aufgerufen werden kann und gleichzeitig die base.FinalizeMethode aufrufen kann
Finalizer sollten nur nicht verwaltete Ressourcen freigeben.
Das Framework garantiert nicht, dass ein Finalizer auf einer bestimmten Instanz überhaupt ausgeführt wird.
Weisen Sie niemals Speicher in Finalisierern zu und rufen Sie niemals virtuelle Methoden von Finalisierern auf.
Vermeiden Sie die Synchronisierung und das Auslösen nicht behandelter Ausnahmen in den Finalisierern.
Die Ausführungsreihenfolge von Finalisierern ist nicht deterministisch. Mit anderen Worten, Sie können sich nicht darauf verlassen, dass noch ein anderes Objekt in Ihrem Finalisierer verfügbar ist.
Definieren Sie keine Finalizer für Werttypen.
Erstellen Sie keine leeren Destruktoren. Mit anderen Worten, Sie sollten niemals explizit einen Destruktor definieren, es sei denn, Ihre Klasse muss nicht verwaltete Ressourcen bereinigen, und wenn Sie eine definieren, sollte sie einige Arbeit leisten. Wenn Sie später nicht mehr nicht verwaltete Ressourcen im Destruktor bereinigen müssen, entfernen Sie sie vollständig.
Entsorgen
Implementieren Sie IDisposableauf jedem Typ, der einen Finalizer hat
Stellen Sie sicher, dass ein Objekt nach dem Aufruf der DisposeMethode unbrauchbar wird. Vermeiden Sie mit anderen Worten die Verwendung eines Objekts, nachdem die DisposeMethode darauf aufgerufen wurde.
Rufen Sie Disposealle IDisposableTypen an, sobald Sie damit fertig sind
Ermöglicht Disposedas mehrfache Aufrufen ohne Fehler.
Unterdrücken Sie spätere Aufrufe des Finalizers aus der DisposeMethode heraus mit der GC.SuppressFinalizeMethode
Vermeiden Sie die Erstellung von verfügbaren Werttypen
Vermeiden Sie es, Ausnahmen innerhalb von DisposeMethoden auszulösen
Entsorgen / Finalisiertes Muster
Microsoft empfiehlt, dass Sie beide implementieren Disposeund Finalizemit nicht verwalteten Ressourcen arbeiten. Die FinalizeImplementierung würde ausgeführt und die Ressourcen würden weiterhin freigegeben, wenn das Objekt durch Müll gesammelt wird, selbst wenn ein Entwickler es versäumt hätte, die DisposeMethode explizit aufzurufen .
Bereinigen Sie die nicht verwalteten Ressourcen in der FinalizeMethode sowie in der DisposeMethode. Rufen Sie außerdem die DisposeMethode für alle .NET-Objekte, die Sie als Komponenten in dieser Klasse haben (mit nicht verwalteten Ressourcen als Mitglied), aus der DisposeMethode auf.
Es gibt einige Schlüssel aus dem Buch MCSD Certification Toolkit (Prüfung 70-483) Seite 193:
destructor ≈ (fast gleich)base.Finalize() , Der Destructor wird in eine Override-Version der Finalize-Methode konvertiert, die den Code des Destructors ausführt und dann die Finalize-Methode der Basisklasse aufruft. Dann ist es völlig nicht deterministisch, dass Sie nicht wissen können, wann aufgerufen wird, da dies von der GC abhängt.
Wenn eine Klasse keine verwalteten Ressourcen und keine nicht verwalteten Ressourcen enthält , sollte sie keinen IDisposableDestruktor implementieren oder haben.
Wenn die Klasse nur Ressourcen verwaltet hat , sollte sie implementiert werden, IDisposableaber keinen Destruktor haben. (Wenn der Destruktor ausgeführt wird, können Sie nicht sicher sein, ob verwaltete Objekte noch vorhanden sind, daher können Sie ihre Dispose()Methoden sowieso nicht aufrufen .)
Wenn die Klasse nur über nicht verwaltete Ressourcen verfügt , muss sie implementiert werden IDisposableund benötigt einen Destruktor, falls das Programm nicht aufruft Dispose().
Dispose()Methode muss sicher sein, um mehr als einmal ausgeführt zu werden. Sie können dies erreichen, indem Sie eine Variable verwenden, um zu verfolgen, ob sie zuvor ausgeführt wurde.
Dispose()sollte sowohl verwaltete als auch nicht verwaltete Ressourcen freigeben .
Der Destruktor sollte nur nicht verwaltete Ressourcen freigeben . Wenn der Destruktor ausgeführt wird, können Sie nicht sicher sein, ob verwaltete Objekte noch vorhanden sind, sodass Sie ihre Dispose-Methoden ohnehin nicht aufrufen können. Dies wird mithilfe des kanonischen protected void Dispose(bool disposing)Musters erreicht, bei dem nur verwaltete Ressourcen freigegeben (entsorgt) werden, wenn disposing == true.
Nach dem Freigeben von Ressourcen Dispose()sollte aufgerufen werdenGC.SuppressFinalize , damit das Objekt die Finalisierungswarteschlange überspringen kann.
Ein Beispiel für eine Implementierung für eine Klasse mit nicht verwalteten und verwalteten Ressourcen:
using System;classDisposableClass:IDisposable{// A name to keep track of the object.publicstringName="";// Free managed and unmanaged resources.publicvoidDispose(){FreeResources(true);// We don't need the destructor because// our resources are already freed.
GC.SuppressFinalize(this);}// Destructor to clean up unmanaged resources// but not managed resources.~DisposableClass(){FreeResources(false);}// Keep track if whether resources are already freed.privateboolResourcesAreFreed=false;// Free resources.privatevoidFreeResources(bool freeManagedResources){Console.WriteLine(Name+": FreeResources");if(!ResourcesAreFreed){// Dispose of managed resources if appropriate.if(freeManagedResources){// Dispose of managed resources here.Console.WriteLine(Name+": Dispose of managed resources");}// Dispose of unmanaged resources here.Console.WriteLine(Name+": Dispose of unmanaged resources");// Remember that we have disposed of resources.ResourcesAreFreed=true;}}}
Das ist eine schöne Antwort! Aber ich denke, das ist falsch: "Der Destruktor sollte GC.SuppressFinalize aufrufen". Sollte die öffentliche Dispose () -Methode nicht stattdessen GC.SuppressFinalize aufrufen? Siehe: docs.microsoft.com/en-us/dotnet/api/… Durch Aufrufen dieser Methode wird verhindert, dass der Garbage Collector Object.Finalize aufruft (das vom Destruktor überschrieben wird).
Ewa
7
In 99% der Fälle sollten Sie sich auch keine Sorgen machen müssen. :) Wenn Ihre Objekte jedoch Verweise auf nicht verwaltete Ressourcen enthalten (z. B. Fensterhandles, Dateihandles), müssen Sie Ihrem verwalteten Objekt eine Möglichkeit bieten, diese Ressourcen freizugeben. Finalize gibt implizite Kontrolle über die Freigabe von Ressourcen. Es wird vom Garbage Collector aufgerufen. Dispose ist eine Möglichkeit, die Freigabe von Ressourcen explizit zu steuern, und kann direkt aufgerufen werden.
Es gibt noch viel mehr über das Thema Garbage Collection zu lernen , aber das ist ein Anfang.
Ich bin mir ziemlich sicher , dass mehr als 1% von C # -Anwendungen verwenden Datenbanken: wo Sie haben über IDisposable SQL Sachen zu kümmern.
Samuel
1
Außerdem sollten Sie IDisposable implementieren, wenn Sie IDisposables kapseln. Was wahrscheinlich die anderen 1% abdeckt.
Darren Clark
@ Samuel: Ich sehe nicht, was Datenbanken damit zu tun haben. Wenn Sie über das Schließen von Verbindungen sprechen, ist das in Ordnung, aber das ist eine andere Sache. Sie müssen keine Objekte entsorgen, um Verbindungen rechtzeitig zu schließen.
JP Alioto
1
@JP: Aber das Using (...) -Muster macht es so viel einfacher, damit umzugehen.
Brody
2
Einverstanden, aber genau das ist der Punkt. Das Verwendungsmuster verbirgt den Aufruf, für Sie zu entsorgen.
JP Alioto
6
Der Finalizer dient zur impliziten Bereinigung. Sie sollten diese Option immer dann verwenden, wenn eine Klasse Ressourcen verwaltet, die unbedingt bereinigt werden müssen , da sonst Handles / Speicher usw. verloren gehen würden.
Die korrekte Implementierung eines Finalizers ist notorisch schwierig und sollte nach Möglichkeit vermieden werden. Die SafeHandleKlasse (verfügbar in .Net v2.0 und höher) bedeutet jetzt, dass Sie einen Finalizer nur noch sehr selten (wenn überhaupt) mehr implementieren müssen.
Die IDisposableBenutzeroberfläche dient zur expliziten Bereinigung und wird viel häufiger verwendet. Sie sollten diese verwenden, damit Benutzer Ressourcen explizit freigeben oder bereinigen können, wenn sie ein Objekt nicht mehr verwenden.
Beachten Sie, dass Sie bei einem Finalizer auch die IDisposableSchnittstelle implementieren sollten , damit Benutzer diese Ressourcen explizit früher freigeben können, als dies bei einer Speicherbereinigung des Objekts der Fall wäre.
Sie schreiben einen Finalizer für Ihre Klasse, wenn er auf nicht verwaltete Ressourcen verweist, und Sie möchten sicherstellen, dass diese nicht verwalteten Ressourcen freigegeben werden, wenn eine Instanz dieser Klasse automatisch durch Speicherbereinigung erfasst
wird . Beachten Sie, dass Sie den Finalizer eines Objekts nicht explizit aufrufen können - er wird vom Garbage Collector automatisch aufgerufen, wenn dies als notwendig erachtet wird.
Auf der anderen Seite implementieren Sie die IDisposable-Schnittstelle (und definieren folglich die Dispose () -Methode als Ergebnis für Ihre Klasse), wenn Ihre Klasse auf nicht verwaltete Ressourcen verweist, aber nicht darauf warten möchten, dass der Garbage Collector aktiviert wird (kann jederzeit sein - nicht unter der Kontrolle des Programmierers) und möchten diese Ressourcen freigeben, sobald Sie fertig sind. Auf diese Weise können Sie nicht verwaltete Ressourcen explizit freigeben, indem Sie die Dispose () -Methode eines Objekts aufrufen.
Ein weiterer Unterschied besteht darin, dass Sie in der Dispose () -Implementierung auch verwaltete Ressourcen freigeben sollten , während dies im Finalizer nicht erfolgen sollte. Dies liegt daran, dass es sehr wahrscheinlich ist, dass die verwalteten Ressourcen, auf die das Objekt verweist, bereits bereinigt wurden, bevor es finalisiert werden kann.
Für eine Klasse, die nicht verwaltete Ressourcen verwendet, empfiehlt es sich, sowohl die Dispose () -Methode als auch den Finalizer als Fallback zu definieren, falls ein Entwickler vergisst, das Objekt explizit zu entsorgen. Beide können eine gemeinsam genutzte Methode verwenden, um verwaltete und nicht verwaltete Ressourcen zu bereinigen:
classClassWithDisposeAndFinalize:IDisposable{// Used to determine if Dispose() has already been called, so that the finalizer// knows if it needs to clean up unmanaged resources.privatebool disposed =false;publicvoidDispose(){// Call our shared helper method.// Specifying "true" signifies that the object user triggered the cleanup.CleanUp(true);// Now suppress finalization to make sure that the Finalize method // doesn't attempt to clean up unmanaged resources.
GC.SuppressFinalize(this);}privatevoidCleanUp(bool disposing){// Be sure we have not already been disposed!if(!this.disposed){// If disposing equals true i.e. if disposed explicitly, dispose all // managed resources.if(disposing){// Dispose managed resources.}// Clean up unmanaged resources here.}
disposed =true;}// the below is called the destructor or Finalizer~ClassWithDisposeAndFinalize(){// Call our shared helper method.// Specifying "false" signifies that the GC triggered the cleanup.CleanUp(false);}
Unterschied zwischen Finalize- und Dispose-Methoden in C #.
GC ruft die Finalize-Methode auf, um die nicht verwalteten Ressourcen (wie Dateibetrieb, Windows-API, Netzwerkverbindung, Datenbankverbindung) zurückzugewinnen. Die Zeit ist jedoch nicht festgelegt, wann GC sie aufrufen würde. Es wird von GC implizit aufgerufen, was bedeutet, dass wir keine Kontrolle auf niedriger Ebene haben.
Entsorgungsmethode: Wir haben eine niedrige Kontrolle darüber, wie wir es aus dem Code aufrufen. Wir können die nicht verwalteten Ressourcen zurückfordern, wenn wir der Meinung sind, dass sie nicht verwendbar sind. Wir können dies erreichen, indem wir das IDisposal-Muster implementieren.
Klasseninstanzen enthalten häufig die Kontrolle über Ressourcen, die nicht von der Laufzeit verwaltet werden, z. B. Fensterhandles (HWND), Datenbankverbindungen usw. Daher sollten Sie sowohl eine explizite als auch eine implizite Möglichkeit zum Freigeben dieser Ressourcen bereitstellen. Stellen Sie implizite Kontrolle bereit, indem Sie die geschützte Finalize-Methode für ein Objekt implementieren (Destruktorsyntax in C # und die verwalteten Erweiterungen für C ++). Der Garbage Collector ruft diese Methode irgendwann auf, nachdem keine gültigen Verweise mehr auf das Objekt vorhanden sind. In einigen Fällen möchten Sie Programmierern, die ein Objekt verwenden, möglicherweise die Möglichkeit geben, diese externen Ressourcen explizit freizugeben, bevor der Garbage Collector das Objekt freigibt. Wenn eine externe Ressource knapp oder teuer ist, kann eine bessere Leistung erzielt werden, wenn der Programmierer Ressourcen explizit freigibt, wenn sie nicht mehr verwendet werden. Implementieren Sie die Dispose-Methode, die von der IDisposable-Schnittstelle bereitgestellt wird, um eine explizite Steuerung bereitzustellen. Der Konsument des Objekts sollte diese Methode aufrufen, wenn das Objekt verwendet wird. Dispose kann auch dann aufgerufen werden, wenn andere Verweise auf das Objekt vorhanden sind.
Beachten Sie, dass Sie auch dann eine implizite Bereinigung mithilfe der Finalize-Methode bereitstellen sollten, wenn Sie eine explizite Steuerung über Dispose bereitstellen. Finalize bietet eine Sicherung, um zu verhindern, dass Ressourcen dauerhaft verloren gehen, wenn der Programmierer Dispose nicht aufruft.
Der Hauptunterschied zwischen Dispose und Finalize besteht darin, dass:
Disposewird normalerweise von Ihrem Code aufgerufen. Die Ressourcen werden sofort freigegeben, wenn Sie es aufrufen. Die Leute vergessen, die Methode aufzurufen, also wird die using() {}Aussage erfunden. Wenn Ihr Programm die Ausführung des Codes innerhalb von beendet hat {}, wird es aufgerufenDispose automatisch Methode auf.
Finalizewird von Ihrem Code nicht aufgerufen. Es soll vom Garbage Collector (GC) aufgerufen werden. Dies bedeutet, dass die Ressource in Zukunft jederzeit freigegeben werden kann, wenn GC dies beschließt. Wenn GC seine Arbeit erledigt, werden viele Finalize-Methoden durchlaufen. Wenn Sie eine starke Logik haben, wird dies den Prozess verlangsamen. Dies kann zu Leistungsproblemen für Ihr Programm führen. Seien Sie also vorsichtig mit dem, was Sie dort eingeben.
Ich persönlich würde den größten Teil der Zerstörungslogik in Dispose schreiben. Hoffentlich klärt dies die Verwirrung.
Wie wir wissen, werden Dispose und Finalize verwendet, um nicht verwaltete Ressourcen freizugeben. Der Unterschied besteht jedoch darin, dass Finalize zwei Zyklen zum Freigeben der Ressourcen verwendet, während Dispose einen Zyklus verwendet.
Entsorgen gibt die Ressource sofort frei. Finalize kann die Ressource mit einem gewissen Grad an Aktualität freigeben oder nicht.
Supercat
1
Ah, er meint wahrscheinlich, dass "ein finalisierbares Objekt vom GC zweimal erkannt werden muss, bevor sein Speicher wiederhergestellt
aeroson
-4
Um auf den ersten Teil zu antworten, sollten Sie Beispiele bereitstellen, bei denen Personen für genau dasselbe Klassenobjekt unterschiedliche Ansätze verwenden. Ansonsten ist es schwierig (oder sogar seltsam) zu antworten.
Mit anderen Worten: Der GC kennt nur den Finalizer (falls vorhanden. Wird Microsoft auch als Destruktor bezeichnet). Ein guter Code versucht, beide zu bereinigen (Finalizer und Dispose).
Antworten:
Andere haben bereits den Unterschied zwischen
Dispose
und behandeltFinalize
(übrigens wird dieFinalize
Methode in der Sprachspezifikation immer noch als Destruktor bezeichnet), daher möchte ich nur ein wenig zu den Szenarien hinzufügen, in denen dieFinalize
Methode nützlich ist.Einige Typen kapseln verfügbare Ressourcen so, dass sie einfach zu verwenden sind, und entsorgen sie in einer einzigen Aktion. Die allgemeine Verwendung ist oft wie folgt: Öffnen, Lesen oder Schreiben, Schließen (Entsorgen). Es passt sehr gut zum
using
Konstrukt.Andere sind etwas schwieriger.
WaitEventHandles
Zum Beispiel werden Instanzen nicht so verwendet, da sie verwendet werden, um von einem Thread zu einem anderen zu signalisieren. Es stellt sich dann die Frage, wer diese anrufen sollDispose
. Als Schutz implementieren solche Typen eineFinalize
Methode, die sicherstellt, dass Ressourcen verfügbar sind, wenn die Instanz nicht mehr von der Anwendung referenziert wird.quelle
Finalize
die gerechtfertigt sein kann, ist, wenn es eine Reihe von Objekten gibt, die daran interessiert sind, eine Ressource am Leben zu erhalten, aber es gibt keine Möglichkeit, mit der ein Objekt, das nicht mehr an der Ressource interessiert ist, herausfinden kann, ob es die ist Letzter. In diesem FallFinalize
wird normalerweise nur ausgelöst, wenn sich niemand für das Objekt interessiert. Das lose Timing vonFinalize
ist für nicht fungible Ressourcen wie Dateien und Sperren schrecklich, für fungible Ressourcen jedoch möglicherweise in Ordnung.Die Finalizer-Methode wird aufgerufen, wenn Ihr Objekt durch Müll gesammelt wird und Sie keine Garantie haben, wann dies geschehen wird (Sie können es erzwingen, aber es beeinträchtigt die Leistung).
Die
Dispose
Methode hingegen soll von dem Code aufgerufen werden, der Ihre Klasse erstellt hat, damit Sie alle Ressourcen, die Sie erworben haben (nicht verwaltete Daten, Datenbankverbindungen, Dateihandles usw.), bereinigen und freigeben können, sobald der Code fertig ist Ihr Objekt.Die Standardpraxis besteht darin, zu implementieren
IDisposable
undDispose
Ihr Objekt in einerusing
Anweisung zu verwenden. Wie zum Beispielusing(var foo = new MyObject()) { }
. Und in Ihrem Finalizer rufen Sie anDispose
, nur für den Fall, dass der aufrufende Code vergessen hat, über Sie zu verfügen.quelle
Finalize ist die Backstop-Methode, die vom Garbage Collector aufgerufen wird, wenn ein Objekt zurückgefordert wird. Dispose ist die "deterministische Bereinigungsmethode", die von Anwendungen aufgerufen wird, um wertvolle native Ressourcen (Fensterhandles, Datenbankverbindungen usw.) freizugeben, wenn sie nicht mehr benötigt werden, anstatt sie unbegrenzt zu belassen, bis der GC das Objekt erreicht.
Als Benutzer eines Objekts verwenden Sie immer Dispose. Finalisieren ist für den GC.
Wenn Sie als Implementierer einer Klasse verwaltete Ressourcen besitzen, die entsorgt werden sollen, implementieren Sie Dispose. Wenn Sie native Ressourcen besitzen, implementieren Sie sowohl Dispose als auch Finalize und rufen beide eine gemeinsame Methode auf, mit der die nativen Ressourcen freigegeben werden. Diese Redewendungen werden normalerweise durch eine private Dispose-Methode (Bool Disposing) kombiniert, bei der Aufrufe mit true entsorgt und Aufrufe mit false abgeschlossen werden. Diese Methode gibt immer native Ressourcen frei, überprüft dann den Dispositionsparameter. Wenn dies zutrifft, werden verwaltete Ressourcen entsorgt und GC.SuppressFinalize aufgerufen.
quelle
Dispose
ist gut und die korrekte Implementierung ist im Allgemeinen einfach.Finalize
ist böse und es richtig zu implementieren ist im Allgemeinen schwierig. Da der GC sicherstellt, dass die Identität eines Objekts niemals "recycelt" wird, solange ein Verweis auf dieses Objekt vorhanden ist, ist es unter anderem einfach, eine Reihe vonDisposable
Objekten zu bereinigen, von denen einige möglicherweise bereits bereinigt wurden kein Problem; Jede Referenz auf ein Objekt, auf dasDispose
bereits aufgerufen wurde, bleibt eine Referenz auf ein Objekt, auf dasDispose
bereits aufgerufen wurde.Fred
das Dateihandle Nr. 42 besitzt und es schließt, hängt das System möglicherweise dieselbe Nummer an das Dateihandle an, das einer anderen Entität zugewiesen wird. In diesem Fall bezieht sich das Dateihandle Nr. 42 nicht auf Freds geschlossene Datei, sondern auf die Datei, die von dieser anderen Entität aktiv verwendet wurde. dennFred
zu versuchen, Griff Nr. 42 wieder zu schließen, wäre katastrophal. Der Versuch, zu 100% zuverlässig zu verfolgen, ob ein nicht verwaltetes Objekt noch freigegeben wurde, ist praktikabel. Der Versuch, mehrere Objekte im Auge zu behalten, ist viel schwieriger.Finalisieren
protected
, nichtpublic
oderprivate
so, dass die Methode nicht direkt aus dem Code der Anwendung aufgerufen werden kann und gleichzeitig diebase.Finalize
Methode aufrufen kannEntsorgen
IDisposable
auf jedem Typ, der einen Finalizer hatDispose
Methode unbrauchbar wird. Vermeiden Sie mit anderen Worten die Verwendung eines Objekts, nachdem dieDispose
Methode darauf aufgerufen wurde.Dispose
alleIDisposable
Typen an, sobald Sie damit fertig sindDispose
das mehrfache Aufrufen ohne Fehler.Dispose
Methode heraus mit derGC.SuppressFinalize
MethodeDispose
Methoden auszulösenEntsorgen / Finalisiertes Muster
Dispose
undFinalize
mit nicht verwalteten Ressourcen arbeiten. DieFinalize
Implementierung würde ausgeführt und die Ressourcen würden weiterhin freigegeben, wenn das Objekt durch Müll gesammelt wird, selbst wenn ein Entwickler es versäumt hätte, dieDispose
Methode explizit aufzurufen .Finalize
Methode sowie in derDispose
Methode. Rufen Sie außerdem dieDispose
Methode für alle .NET-Objekte, die Sie als Komponenten in dieser Klasse haben (mit nicht verwalteten Ressourcen als Mitglied), aus derDispose
Methode auf.quelle
Finalize wird vom GC aufgerufen, wenn dieses Objekt nicht mehr verwendet wird.
Dispose ist nur eine normale Methode, die der Benutzer dieser Klasse aufrufen kann, um Ressourcen freizugeben.
Wenn der Benutzer vergessen hat, Dispose aufzurufen, und wenn die Klasse Finalize implementiert hat, stellt GC sicher, dass sie aufgerufen wird.
quelle
Es gibt einige Schlüssel aus dem Buch MCSD Certification Toolkit (Prüfung 70-483) Seite 193:
destructor ≈ (fast gleich)
base.Finalize()
, Der Destructor wird in eine Override-Version der Finalize-Methode konvertiert, die den Code des Destructors ausführt und dann die Finalize-Methode der Basisklasse aufruft. Dann ist es völlig nicht deterministisch, dass Sie nicht wissen können, wann aufgerufen wird, da dies von der GC abhängt.Wenn eine Klasse keine verwalteten Ressourcen und keine nicht verwalteten Ressourcen enthält , sollte sie keinen
IDisposable
Destruktor implementieren oder haben.Wenn die Klasse nur Ressourcen verwaltet hat , sollte sie implementiert werden,
IDisposable
aber keinen Destruktor haben. (Wenn der Destruktor ausgeführt wird, können Sie nicht sicher sein, ob verwaltete Objekte noch vorhanden sind, daher können Sie ihreDispose()
Methoden sowieso nicht aufrufen .)Wenn die Klasse nur über nicht verwaltete Ressourcen verfügt , muss sie implementiert werden
IDisposable
und benötigt einen Destruktor, falls das Programm nicht aufruftDispose()
.Dispose()
Methode muss sicher sein, um mehr als einmal ausgeführt zu werden. Sie können dies erreichen, indem Sie eine Variable verwenden, um zu verfolgen, ob sie zuvor ausgeführt wurde.Dispose()
sollte sowohl verwaltete als auch nicht verwaltete Ressourcen freigeben .Der Destruktor sollte nur nicht verwaltete Ressourcen freigeben . Wenn der Destruktor ausgeführt wird, können Sie nicht sicher sein, ob verwaltete Objekte noch vorhanden sind, sodass Sie ihre Dispose-Methoden ohnehin nicht aufrufen können. Dies wird mithilfe des kanonischen
protected void Dispose(bool disposing)
Musters erreicht, bei dem nur verwaltete Ressourcen freigegeben (entsorgt) werden, wenndisposing == true
.Nach dem Freigeben von Ressourcen
Dispose()
sollte aufgerufen werdenGC.SuppressFinalize
, damit das Objekt die Finalisierungswarteschlange überspringen kann.Ein Beispiel für eine Implementierung für eine Klasse mit nicht verwalteten und verwalteten Ressourcen:
quelle
In 99% der Fälle sollten Sie sich auch keine Sorgen machen müssen. :) Wenn Ihre Objekte jedoch Verweise auf nicht verwaltete Ressourcen enthalten (z. B. Fensterhandles, Dateihandles), müssen Sie Ihrem verwalteten Objekt eine Möglichkeit bieten, diese Ressourcen freizugeben. Finalize gibt implizite Kontrolle über die Freigabe von Ressourcen. Es wird vom Garbage Collector aufgerufen. Dispose ist eine Möglichkeit, die Freigabe von Ressourcen explizit zu steuern, und kann direkt aufgerufen werden.
Es gibt noch viel mehr über das Thema Garbage Collection zu lernen , aber das ist ein Anfang.
quelle
Der Finalizer dient zur impliziten Bereinigung. Sie sollten diese Option immer dann verwenden, wenn eine Klasse Ressourcen verwaltet, die unbedingt bereinigt werden müssen , da sonst Handles / Speicher usw. verloren gehen würden.
Die korrekte Implementierung eines Finalizers ist notorisch schwierig und sollte nach Möglichkeit vermieden werden. Die
SafeHandle
Klasse (verfügbar in .Net v2.0 und höher) bedeutet jetzt, dass Sie einen Finalizer nur noch sehr selten (wenn überhaupt) mehr implementieren müssen.Die
IDisposable
Benutzeroberfläche dient zur expliziten Bereinigung und wird viel häufiger verwendet. Sie sollten diese verwenden, damit Benutzer Ressourcen explizit freigeben oder bereinigen können, wenn sie ein Objekt nicht mehr verwenden.Beachten Sie, dass Sie bei einem Finalizer auch die
IDisposable
Schnittstelle implementieren sollten , damit Benutzer diese Ressourcen explizit früher freigeben können, als dies bei einer Speicherbereinigung des Objekts der Fall wäre.Unter DG Update: Entsorgung, Finalisierung und Ressourcenverwaltung finden Sie die meiner Meinung nach besten und vollständigsten Empfehlungen zu Finalisierern und
IDisposable
.quelle
Die Zusammenfassung lautet -
Ein weiterer Unterschied besteht darin, dass Sie in der Dispose () -Implementierung auch verwaltete Ressourcen freigeben sollten , während dies im Finalizer nicht erfolgen sollte. Dies liegt daran, dass es sehr wahrscheinlich ist, dass die verwalteten Ressourcen, auf die das Objekt verweist, bereits bereinigt wurden, bevor es finalisiert werden kann.
Für eine Klasse, die nicht verwaltete Ressourcen verwendet, empfiehlt es sich, sowohl die Dispose () -Methode als auch den Finalizer als Fallback zu definieren, falls ein Entwickler vergisst, das Objekt explizit zu entsorgen. Beide können eine gemeinsam genutzte Methode verwenden, um verwaltete und nicht verwaltete Ressourcen zu bereinigen:
quelle
Das beste Beispiel, das ich kenne.
quelle
Unterschied zwischen Finalize- und Dispose-Methoden in C #.
GC ruft die Finalize-Methode auf, um die nicht verwalteten Ressourcen (wie Dateibetrieb, Windows-API, Netzwerkverbindung, Datenbankverbindung) zurückzugewinnen. Die Zeit ist jedoch nicht festgelegt, wann GC sie aufrufen würde. Es wird von GC implizit aufgerufen, was bedeutet, dass wir keine Kontrolle auf niedriger Ebene haben.
Entsorgungsmethode: Wir haben eine niedrige Kontrolle darüber, wie wir es aus dem Code aufrufen. Wir können die nicht verwalteten Ressourcen zurückfordern, wenn wir der Meinung sind, dass sie nicht verwendbar sind. Wir können dies erreichen, indem wir das IDisposal-Muster implementieren.
quelle
Klasseninstanzen enthalten häufig die Kontrolle über Ressourcen, die nicht von der Laufzeit verwaltet werden, z. B. Fensterhandles (HWND), Datenbankverbindungen usw. Daher sollten Sie sowohl eine explizite als auch eine implizite Möglichkeit zum Freigeben dieser Ressourcen bereitstellen. Stellen Sie implizite Kontrolle bereit, indem Sie die geschützte Finalize-Methode für ein Objekt implementieren (Destruktorsyntax in C # und die verwalteten Erweiterungen für C ++). Der Garbage Collector ruft diese Methode irgendwann auf, nachdem keine gültigen Verweise mehr auf das Objekt vorhanden sind. In einigen Fällen möchten Sie Programmierern, die ein Objekt verwenden, möglicherweise die Möglichkeit geben, diese externen Ressourcen explizit freizugeben, bevor der Garbage Collector das Objekt freigibt. Wenn eine externe Ressource knapp oder teuer ist, kann eine bessere Leistung erzielt werden, wenn der Programmierer Ressourcen explizit freigibt, wenn sie nicht mehr verwendet werden. Implementieren Sie die Dispose-Methode, die von der IDisposable-Schnittstelle bereitgestellt wird, um eine explizite Steuerung bereitzustellen. Der Konsument des Objekts sollte diese Methode aufrufen, wenn das Objekt verwendet wird. Dispose kann auch dann aufgerufen werden, wenn andere Verweise auf das Objekt vorhanden sind.
Beachten Sie, dass Sie auch dann eine implizite Bereinigung mithilfe der Finalize-Methode bereitstellen sollten, wenn Sie eine explizite Steuerung über Dispose bereitstellen. Finalize bietet eine Sicherung, um zu verhindern, dass Ressourcen dauerhaft verloren gehen, wenn der Programmierer Dispose nicht aufruft.
quelle
Der Hauptunterschied zwischen Dispose und Finalize besteht darin, dass:
Dispose
wird normalerweise von Ihrem Code aufgerufen. Die Ressourcen werden sofort freigegeben, wenn Sie es aufrufen. Die Leute vergessen, die Methode aufzurufen, also wird dieusing() {}
Aussage erfunden. Wenn Ihr Programm die Ausführung des Codes innerhalb von beendet hat{}
, wird es aufgerufenDispose
automatisch Methode auf.Finalize
wird von Ihrem Code nicht aufgerufen. Es soll vom Garbage Collector (GC) aufgerufen werden. Dies bedeutet, dass die Ressource in Zukunft jederzeit freigegeben werden kann, wenn GC dies beschließt. Wenn GC seine Arbeit erledigt, werden viele Finalize-Methoden durchlaufen. Wenn Sie eine starke Logik haben, wird dies den Prozess verlangsamen. Dies kann zu Leistungsproblemen für Ihr Programm führen. Seien Sie also vorsichtig mit dem, was Sie dort eingeben.Ich persönlich würde den größten Teil der Zerstörungslogik in Dispose schreiben. Hoffentlich klärt dies die Verwirrung.
quelle
Wie wir wissen, werden Dispose und Finalize verwendet, um nicht verwaltete Ressourcen freizugeben. Der Unterschied besteht jedoch darin, dass Finalize zwei Zyklen zum Freigeben der Ressourcen verwendet, während Dispose einen Zyklus verwendet.
quelle
Um auf den ersten Teil zu antworten, sollten Sie Beispiele bereitstellen, bei denen Personen für genau dasselbe Klassenobjekt unterschiedliche Ansätze verwenden. Ansonsten ist es schwierig (oder sogar seltsam) zu antworten.
Was die zweite Frage betrifft, lesen Sie besser zuerst diese ordnungsgemäße Verwendung der IDisposable-Schnittstelle, die dies behauptet
Mit anderen Worten: Der GC kennt nur den Finalizer (falls vorhanden. Wird Microsoft auch als Destruktor bezeichnet). Ein guter Code versucht, beide zu bereinigen (Finalizer und Dispose).
quelle