Was ist in C # der Unterschied zwischen einem Destruktor und einer Finalize-Methode in einer Klasse?

96

Was ist der Unterschied zwischen einem Destruktor und einer Finalize-Methode in einer Klasse, wenn es einen gibt?

Ich habe kürzlich festgestellt, dass Visual Studio 2008 einen Destruktor als Synonym für eine Finalize-Methode betrachtet. Dies bedeutet, dass Sie in Visual Studio nicht beide Methoden gleichzeitig in einer Klasse definieren können.

Zum Beispiel das folgende Codefragment:

class TestFinalize
{
    ~TestFinalize()
    {
        Finalize();
    }

    public bool Finalize()
    {
        return true;
    }
}

Gibt beim Aufruf von Finalize im Destruktor den folgenden Fehler aus:

Der Aufruf ist zwischen den folgenden Methoden oder Eigenschaften nicht eindeutig: 'TestFinalize. ~ TestFinalize ()' und 'TestFinalize.Finalize ()'

Wenn der Aufruf zum Finalisieren auskommentiert ist, wird der folgende Fehler ausgegeben:

Der Typ 'ManagementConcepts.Service.TestFinalize' definiert bereits ein Mitglied namens 'Finalize' mit denselben Parametertypen

Jeff Leonard
quelle

Antworten:

68

Ein Destruktor in C # überschreibt die System.Object.FinalizeMethode. Sie müssen dazu die Destruktorsyntax verwenden. Durch manuelles Überschreiben Finalizewird eine Fehlermeldung angezeigt.

Im Grunde , was Sie versuchen , mit Ihrer zu tun FinalizeMethode Erklärung versteckt die Methode der Basisklasse. Der Compiler gibt dann eine Warnung aus, die mit dem newModifikator stummgeschaltet werden kann (falls dies funktionieren sollte). Hierbei ist zu beachten, dass Sie nicht beide gleichzeitig overrideein newMitglied mit identischem Namen deklarieren können. Wenn FinalizeSie also sowohl einen Destruktor als auch eine Methode haben, führt dies zu einem Fehler (Sie können jedoch eine public new void Finalize()Methode deklarieren, wenn dies nicht empfohlen wird Sie deklarieren keinen Destruktor).

Mehrdad Afshari
quelle
71

Wikipedia hat einige gute Diskussionen über den Unterschied zwischen einem Finalizer und einem Destruktor im Finalizer- Artikel.

C # hat wirklich keinen "wahren" Destruktor. Die Syntax ähnelt einem C ++ - Destruktor, ist aber wirklich ein Finalizer. Sie haben es im ersten Teil Ihres Beispiels richtig geschrieben:

~ClassName() { }

Das Obige ist syntaktischer Zucker für eine FinalizeFunktion. Es stellt sicher, dass die Finalizer in der Basis garantiert ausgeführt werden, ist aber ansonsten identisch mit dem Überschreiben der FinalizeFunktion. Dies bedeutet, dass Sie beim Schreiben der Destruktorsyntax wirklich den Finalizer schreiben.

Laut Microsoft bezieht sich der Finalizer auf die Funktion, die der Garbage Collector beim Sammeln von ( Finalize) aufruft , während der Destruktor Ihr Code ist, der als Ergebnis ausgeführt wird (der syntaktische Zucker, der wird Finalize). Sie sind so nah dran, dass Microsoft niemals die Unterscheidung hätte treffen dürfen.

Die Verwendung des C ++ - Begriffs "Destruktor" durch Microsoft ist irreführend, da er in C ++ auf demselben Thread ausgeführt wird, sobald das Objekt gelöscht oder vom Stapel entfernt wird, während er in C # zu einem anderen Zeitpunkt auf einem separaten Thread ausgeführt wird.

Kenzi
quelle
Ich würde argumentieren, dass eine solche Unterscheidung zwischen Destruktor und Finalisierer wichtig ist. Allerdings würden sich nur diejenigen um diese Unterscheidung kümmern, die sich darum kümmern, was unter der Haube vor sich geht.
Kyle Baran
1
Beachten Sie auch, dass ECMA-334 "Destruktor" und "Finalisierer" vor langer Zeit offiziell eindeutig eindeutig definiert hat. Ich weiß nicht, warum MS in ihren Spezifikationen immer noch auf dem irreführenden Begriff besteht.
FrankHB
Zumindest aus der Arbeit mit Mono wird C # tatsächlich nach C ++ modelliert, und die meisten nativen C # -Objekte sind C ++ - Objekte. Die Art und Weise, wie der Compiler, der Mono kompiliert hat, funktioniert, bestimmt, wie diese C ++ - Objekte zerstört werden und wie sich die C # -Objekt-Finalisierung auf C ++ ausbreitet und diese Destruktoren aufruft. Die Unterscheidung macht unter der Haube Sinn, trifft aber immer noch nicht wirklich auf C # selbst zu.
Kenzi
20

Hier zu finden: http://sanjaysainitech.blogspot.com/2007/06/difference-between-destructor-dispose.html

  1. Zerstörer

    Dies sind spezielle Methoden, die Bereinigungscode für das Objekt enthalten. Sie können sie in Ihrem Code nicht explizit aufrufen, da sie von GC implizit aufgerufen werden. In C # haben sie denselben Namen wie der Klassenname, dem das ~Vorzeichen vorangestellt ist . Mögen-

    Class MyClass
    {
    
    ~MyClass()
    {
    .....
    }
    }

    In VB.NET werden Destruktoren implementiert, indem die Finalize-Methode der System.Object-Klasse überschrieben wird.

  2. Entsorgen

    Diese sind wie alle anderen Methoden in der Klasse und können explizit aufgerufen werden, haben jedoch einen besonderen Zweck, das Objekt zu bereinigen. In der dispose-Methode schreiben wir Bereinigungscode für das Objekt. Es ist wichtig, dass wir alle nicht verwalteten Ressourcen in der Dispose-Methode wie Datenbankverbindung, Dateien usw. freigegeben haben. Die Klasse, die die Dispose-Methode implementiert, sollte die IDisposable-Schnittstelle implementieren. Eine Dispose-Methode sollte die GC.SuppressFinalize-Methode für das Objekt aufrufen, das sie entsorgt, wenn die class hat desturctor, weil es bereits die Arbeit zum Bereinigen des Objekts erledigt hat. Dann muss der Garbage Collector die Finalize-Methode des Objekts nicht aufrufen. Referenz: http://msdn2.microsoft.com/en-us/library/aa720161(VS.71).aspx

  3. Finalisieren

    Eine Finalize-Methode dient als Schutz für die Bereinigung von Ressourcen, falls Ihre Dispose-Methode nicht aufgerufen wird. Sie sollten nur eine Finalize-Methode implementieren, um nicht verwaltete Ressourcen zu bereinigen. Sie sollten keine Finalize-Methode für verwaltete Objekte implementieren, da der Garbage Collector verwaltete Ressourcen automatisch bereinigt. Die Finalize-Methode wird vom GC implizit aufgerufen, daher können Sie sie nicht aus Ihrem Code heraus aufrufen.

    Hinweis: In C # kann die Finalize-Methode nicht überschrieben werden. Sie müssen daher einen Destruktor verwenden, dessen interne Implementierung die Finalize-Methode in MSIL überschreibt. In VB.NET kann die Finalize-Methode jedoch überschrieben werden, da sie die Destruktormethode unterstützt.

Update: Interessanter semi-verwandter Thread hier .

Andrew Siemer
quelle
1
You should only implement a Finalize method to clean up unmanaged resources: Sie setzen es in Finalize. Gleiches gilt für Entsorgen?
Hqt
@hqt: Fälle, in denen implementiert werden sollte, sind Disposeweitaus zahlreicher als Fälle, in denen ein Finalizer implementiert werden sollte. Implementieren Sie, Disposewenn es wahrscheinlich ist, dass eine Instanz der Klasse oder eine abgeleitete Klasse das Letzte ist, das entweder direkt eine nicht verwaltete Ressource besitzt oder direkt das Letzte, das eine nicht verwaltete Ressource direkt besitzt, oder direkt das Letzte, das direkt besitzt usw. Nur Finalizezur Ressourcenbereinigung implementieren, wenn die eigene Klasse <i> direkt </ i> eine nicht verwaltete Ressource <i> besitzt und fast nichts anderes </ i> - ein viel engeres Szenario.
Supercat
@hqt: Wenn eine Klasse direkt nicht verwaltete Ressourcen besitzt und auch Verweise auf andere Objekte enthält, sollten die nicht verwalteten Ressourcen im Allgemeinen in ihre eigene finalisierbare Klasse aufgeteilt werden (die im Idealfall keine starken Verweise auf etwas anderes enthalten sollte), dh die Klasse die Verweise auf andere Objekte enthält, würde nur "Dinge besitzen, die direkt nicht verwaltete Ressourcen besitzen", anstatt die Ressourcen selbst zu besitzen, und würde daher keinen Finalizer benötigen.
Supercat