2009-09-10 10 views
6

Ich las this Artikel neulich und fragte mich, warum es einen Finalizer mit der Dispose-Methode gab. Ich habe unter here unter SO gelesen, warum Sie Dispose zum Finalizer hinzufügen sollten. Meine Neugier ist, wann würde der Finalizer über die Dispose-Methode selbst aufgerufen werden? Gibt es ein Codebeispiel oder basiert es auf etwas, das auf dem System passiert, auf dem die Software läuft? Wenn dies der Fall ist, könnte die Dispose-Methode vom GC ausgeführt werden.Wann würde entsorgen Methode nicht aufgerufen werden?

Antwort

9

Der Zweck des Finalisierers hier ist einfach eine Sicherheitsvorkehrung gegen Speicherlecks (wenn Sie nicht explizit Dispose anrufen). Es bedeutet auch, dass Sie Ihre Objekte nicht entsorgen müssen, wenn Sie beim Herunterfahren des Programms Ressourcen freigeben möchten, da der GC gezwungen ist, alle Objekte zu finalisieren und alle Objekte zu sammeln.

Als ein verwandter Punkt ist es wichtig, das Objekt vom Finisizer etwas anders zu entsorgen.

~MyClass() 
{ 
    Dispose(false); 
} 

public void Dispose() 
{ 
    Dispose(true); 
    GC.SuppressFinalize(this); 
} 

protected void Dispose(disposing) 
{ 
    if (!this.disposed) 
    { 
     if (disposing) 
     { 
      // Dispose managed resources here. 
     } 
     // Dispose unmanaged resources here. 
    } 
    this.disposed = true; 
} 

Der Grund, warum Sie wollen nicht verwalteten Ressourcen in Ihrem finaliser entsorgen ist, dass Sie sich tatsächlich stark Verweise auf sich dabei, erschaffen und dies könnte die GC zu tun, es ist Arbeit richtig verhindern und das Sammeln Sie. Unverwaltete Ressourcen (z. B. Win32-Handles und dergleichen) sollten natürlich immer explizit geschlossen/entsorgt werden, da die CLR keine Kenntnis von ihnen hat.

+1

Und noch ein Grund, verwaltete Ressourcen nicht in Ihrem Finalisierer zu verwahren ... Es ist möglich, dass sie bereits zu dem Zeitpunkt, zu dem Ihr Finaliser ausgeführt wird, gecodiert wurden. Wenn Sie versuchen, sie nach der Erfassung zu entsorgen, würde dies zu einem Laufzeitfehler führen. – LukeH

+1

@Luke: True, aber das kann leicht genug vermieden werden, indem Sie alle Ihre Referenzen auf Null setzen und dann einen Null-Check vor dem Entsorgen durchführen. – Noldorin

+0

@Noldorin - Wo würde die Aufhebung der Registrierung von Ereignissen in Ihrem Beispiel fallen? Ich verstehe technisch, dass sie unter verwaltet werden würden, aber was wäre, wenn wir über ein Ereignis ein Objekt an diese Klasse gebunden hätten und es nicht im nicht verwalteten Teil aufheben würden (vorausgesetzt, der Benutzer ruft nicht direkt Dispose und das linke zum GC auf, um es zu bereinigen) oben). Wäre es sicher/in Ordnung, die Registrierung des Ereignisses in den verwalteten Bereich zu verschieben, um sicherzustellen, dass dies geschieht? Der Nebeneffekt könnte sein, dass jemand denkt, dass sie ein Objekt disponieren, aber wirklich wird es nie wegen der Ereignisverbindung zwischen dieser Klasse und der anderen disponiert. – SwDevMan81

4

Dies ist meistens da, um sich zu schützen. Sie können nicht diktieren, was der Endbenutzer Ihrer Klasse tun wird. Durch das Bereitstellen eines Finalizers zusätzlich zu einer Dispose-Methode wird der GC Ihr Objekt "dispose", wodurch Ihre Ressourcen entsprechend freigegeben werden, selbst wenn der Benutzer vergisst, Dispose() aufzurufen oder Ihre Klasse falsch verwendet.

+1

Es ist erwähnenswert, dass der GC nicht deterministisch ist, also gibt es keine Garantie dafür, wann * oder sogar wenn * Ihr Finaliser aufgerufen wird. – LukeH

+0

Ja - Wenn Ihr Programm lange genug läuft, wird Ihr Objekt am wahrscheinlichsten finalisiert. Auch wenn es sauber heruntergefahren wird, wird es abgeschlossen. Aber es gibt keine Garantien mit dem GC - das ist einer der Gründe, warum IDisposable überhaupt existiert. –

1

Die dispose-Methode muss explizit aufgerufen werden, entweder durch Aufrufen von Dispose() oder durch Verwenden des Objekts in einer using-Anweisung. Der GC ruft immer den Finalizer auf. Wenn also etwas passieren muss, bevor die Objekte entfernt werden, sollte der Finalizer zumindest überprüfen, ob alles im Objekt bereinigt ist.

Sie möchten möglichst keine Objekte im Finalizer bereinigen, da dies zu Mehrarbeit im Vergleich zur Entsorgung führt (wie Aufruf von dispose), aber Sie sollten immer den Finalizer einchecken, wenn Objekte vorhanden sind herumliegen, die entfernt werden müssen.

2

Der Finalizer wird aufgerufen, wenn das Objekt als Garbage Collection erfasst wird. Dispose muss explizit aufgerufen werden. Im folgenden Code wird der Finalizer aufgerufen, die Dispose-Methode jedoch nicht.

class Foo : IDisposable 
{ 
    public void Dispose() 
    { 
    Console.WriteLine("Disposed"); 
    } 

    ~Foo() 
    { 
    Console.WriteLine("Finalized"); 
    } 
} 

... 

public void Go() 
{ 
    Foo foo = new Foo(); 
} 
+1

Das ist nicht ganz richtig. Der Finalizer wird einige Zeit aufgerufen, nachdem das Objekt andernfalls für die Speicherbereinigung geeignet wäre (d. H. Die Anwendung verweist nicht mehr auf die Instanz). Da der Finalizer jedoch für die Instanz ausgeführt werden muss, wird das Objekt von der CLR tatsächlich rooted, und es wird daher keine Garbage-Collection durchgeführt, bis der Finalizer ausgeführt wurde. –

+0

Es gibt auch keine Garantie, dass ein Objekt * jemals * gecodiert wird oder dass sein Finalizer * jemals * aufgerufen wird. Deshalb ist es doppelt wichtig, dass Sie alle "IDisposable" Objekte korrekt entsorgen. – LukeH

0

Eine wichtige, aber subtile Note noch nicht erwähnt: ein selten betrachtet Zweck der Entsorgung ist von einem Objekt zu verhindern vorzeitig gereinigt werden. Objekte mit Finalizern müssen sorgfältig geschrieben werden, damit kein Finalizer früher als erwartet ausgeführt wird. Ein Finalizer kann nicht vor dem Beginn des letzten Methodenaufrufs für ein Objekt (*) ausgeführt werden. Unter wird jedoch möglicherweise der letzte Methodenaufruf ausgeführt, wenn das Objekt nach Abschluss der Methode abgebrochen wird. Code, der ein Objekt ordnungsgemäß entsorgen kann, kann das Objekt vor dem Aufruf von Dispose nicht verlassen. Es besteht also keine Gefahr, dass ein Finalizer Code verwüstet, der ordnungsgemäß Dispose verwendet. Wenn andererseits die letzte Methode zur Verwendung eines Objekts Entitäten verwendet, die nach der letzten Verwendung der Objektreferenz im Finalizer bereinigt werden, ist es möglich, dass der Garbage-Collector Finalize für das Objekt aufruft und bereinigt up Entitäten, die noch verwendet werden.Die Abhilfe besteht darin sicherzustellen, dass jeder Aufrufmethode, die Entitäten verwendet, die von einem Finalisierer bereinigt werden, irgendwann ein Methodenaufruf folgt, der "dieses" verwendet. GC.KeepAlive (dies) ist eine gute Methode dafür.

(*) Nicht virtuelle Methoden, die zu Inline-Code erweitert werden, der nichts mit dem Objekt zu tun hat, können von dieser Regel ausgenommen sein, aber Dispose ist normalerweise eine virtuelle Methode oder ruft sie auf.