Warum verwenden manche Leute die Finalize
Methode gegenüber der Dispose
Methode?
In welchen Situationen würden Sie die Finalize
Methode gegenüber der Dispose
Methode verwenden und umgekehrt?
Warum verwenden manche Leute die Finalize
Methode gegenüber der Dispose
Methode?
In welchen Situationen würden Sie die Finalize
Methode gegenüber der Dispose
Methode verwenden und umgekehrt?
Antworten:
Andere haben bereits den Unterschied zwischen Dispose
und behandelt Finalize
(übrigens wird die Finalize
Methode in der Sprachspezifikation immer noch als Destruktor bezeichnet), daher möchte ich nur ein wenig zu den Szenarien hinzufügen, in denen die Finalize
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 soll Dispose
. Als Schutz implementieren solche Typen eine Finalize
Methode, die sicherstellt, dass Ressourcen verfügbar sind, wenn die Instanz nicht mehr von der Anwendung referenziert wird.
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 Fall Finalize
wird normalerweise nur ausgelöst, wenn sich niemand für das Objekt interessiert. Das lose Timing von Finalize
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
und Dispose
Ihr Objekt in einer using
Anweisung 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.
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.
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 von Disposable
Objekten zu bereinigen, von denen einige möglicherweise bereits bereinigt wurden kein Problem; Jede Referenz auf ein Objekt, auf das Dispose
bereits aufgerufen wurde, bleibt eine Referenz auf ein Objekt, auf das Dispose
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. denn Fred
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
, nicht public
oder private
so, dass die Methode nicht direkt aus dem Code der Anwendung aufgerufen werden kann und gleichzeitig die base.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 die Dispose
Methode darauf aufgerufen wurde.Dispose
alle IDisposable
Typen an, sobald Sie damit fertig sindDispose
das mehrfache Aufrufen ohne Fehler.Dispose
Methode heraus mit der GC.SuppressFinalize
MethodeDispose
Methoden auszulösenEntsorgen / Finalisiertes Muster
Dispose
und Finalize
mit nicht verwalteten Ressourcen arbeiten. Die Finalize
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, die Dispose
Methode explizit aufzurufen .Finalize
Methode sowie in der Dispose
Methode. Rufen Sie außerdem die Dispose
Methode für alle .NET-Objekte, die Sie als Komponenten in dieser Klasse haben (mit nicht verwalteten Ressourcen als Mitglied), aus der Dispose
Methode auf.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.
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 ihre Dispose()
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 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;
class DisposableClass : IDisposable
{
// A name to keep track of the object.
public string Name = "";
// Free managed and unmanaged resources.
public void Dispose()
{
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.
private bool ResourcesAreFreed = false;
// Free resources.
private void FreeResources(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;
}
}
}
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.
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
.
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:
class ClassWithDisposeAndFinalize : IDisposable
{
// Used to determine if Dispose() has already been called, so that the finalizer
// knows if it needs to clean up unmanaged resources.
private bool disposed = false;
public void Dispose()
{
// 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);
}
private void CleanUp(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);
}
Das beste Beispiel, das ich kenne.
public abstract class DisposableType: IDisposable
{
bool disposed = false;
~DisposableType()
{
if (!disposed)
{
disposed = true;
Dispose(false);
}
}
public void Dispose()
{
if (!disposed)
{
disposed = true;
Dispose(true);
GC.SuppressFinalize(this);
}
}
public void Close()
{
Dispose();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// managed objects
}
// unmanaged objects and resources
}
}
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:
Dispose
wird 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.
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.
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.
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
Es ist deine Wahl! Aber wählen Sie Entsorgen.
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).