Best Practices für die Verwendung von Dispose and Finalize in .Net

Microsoft .Net Framework bietet einen Garbage Collector, der im Hintergrund ausgeführt wird und den von verwalteten Objekten belegten Speicher freigibt, wenn in Ihrem Code nicht mehr auf sie verwiesen wird. Obwohl der Garbage Collector in der Lage ist, den von verwalteten Objekten belegten Speicher zu bereinigen, kann nicht garantiert werden, dass der von nicht verwalteten Objekten belegte Speicher bei Ausführung des nächsten GC-Zyklus bereinigt wird. Wenn Ihre Anwendung nicht verwaltete Ressourcen enthält, sollten Sie sicherstellen, dass Sie diese Ressourcen explizit freigeben, wenn Sie sie nicht mehr verwenden. In diesem Artikel werde ich die Best Practices hervorheben, die Sie zum Bereinigen der in Ihrer Anwendung verwendeten Ressourcen befolgen sollten.

Der GC verwendet Generationen, um die relative Lebensdauer von Objekten zu verwalten, die im Speicher erstellt werden. Neu erstellte Objekte werden in Generation 0 platziert. Die Grundannahme ist, dass ein neu erstelltes Objekt eine kürzere Lebensdauer haben kann, während ein altes Objekt eine längere Lebensdauer haben kann. Wenn Objekte in Generation 0 nach einem GC-Zyklus nicht zurückgefordert werden, werden sie in Generation 1 verschoben. Wenn Objekte in Generation 1 eine GC-Bereinigung überleben, werden sie in Generation 2 verschoben. Beachten Sie, dass der GC in der Generation 1 häufiger ausgeführt wird niedere Generationen als die höheren. Objekte, die sich in Generation 0 befinden, werden also häufiger bereinigt als Objekte, die sich in Generation 1 befinden.Es ist eine bessere Programmierpraxis, um sicherzustellen, dass Sie mehr lokale Objekte als Objekte im höheren Bereich verwenden, um zu verhindern, dass Objekte in höhere Generationen verschoben werden.

Beachten Sie, dass ein Destruktor in Ihrer Klasse von der Laufzeit als Finalize () -Methode behandelt wird. Da die Finalisierung kostspielig ist, sollten Sie Destruktoren nur bei Bedarf verwenden - wenn Sie einige Ressourcen in Ihrer Klasse haben, die Sie bereinigen müssten. Wenn Sie einen Finalizer in Ihrer Klasse haben, werden Objekte dieser Klassen in die Finalisierungswarteschlange verschoben. Wenn die Objekte erreichbar sind, werden sie in die Warteschlange "Freachable" verschoben. Der GC stellt den Speicher wieder her, der von Objekten belegt wird, die nicht erreichbar sind. Der GC prüft regelmäßig, ob die Objekte in der Warteschlange "Freachable" erreichbar sind. Wenn sie nicht erreichbar sind, wird der von diesen Objekten belegte Speicher zurückgefordert. Es ist also offensichtlich, dass Objekte, die sich in der Warteschlange "Freachable" befinden, mehr Zeit benötigen würden, um vom Garbage Collector bereinigt zu werden.Es ist eine schlechte Praxis, leere Destruktoren in Ihrer C # -Klasse zu haben, da Objekte für solche Klassen in die Finalisierungswarteschlange und gegebenenfalls in die Warteschlange "Freachable" verschoben werden.

Ein Finalizer wird implizit aufgerufen, wenn der vom Objekt belegte Speicher zurückgefordert wird. Es ist jedoch nicht garantiert, dass ein Finalizer vom GC aufgerufen wird - er kann oder kann überhaupt nicht aufgerufen werden. Im Wesentlichen arbeitet ein Finalizer in einem nicht deterministischen Modus - die Laufzeit garantiert nicht, dass ein Finalizer überhaupt aufgerufen wird. Sie können jedoch den Aufruf des Finalizers erzwingen, obwohl dies überhaupt keine gute Vorgehensweise ist, da Leistungseinbußen verbunden sind. Finalizer sollten immer geschützt sein und immer nur zum Bereinigen verwalteter Ressourcen verwendet werden. Sie sollten niemals Speicher im Finalizer zuweisen, Code schreiben, um die Thread-Sicherheit zu implementieren, oder virtuelle Methoden aus einem Finalizer heraus aufrufen.

Die Dispose-Methode bietet andererseits einen "deterministischen Bereinigungs" -Ansatz für die Ressourcenbereinigung in .Net. Die Dispose-Methode sollte jedoch im Gegensatz zum Finalizer explizit aufgerufen werden. Wenn Sie eine Dispose-Methode in einer Klasse definiert haben, sollten Sie sicherstellen, dass sie aufgerufen wird. Daher sollte die Dispose-Methode vom Client-Code explizit aufgerufen werden. Was aber, wenn Sie vergessen, die Dispose-Methode aufzurufen, die von einer Klasse verfügbar gemacht wird, die nicht verwaltete Ressourcen verwendet? Clients einer Instanz einer Klasse, die die IDisposable-Schnittstelle implementiert, sollten die Dispose-Methode explizit aufrufen. In diesem Fall müssen Sie Dispose im Finalizer aufrufen. Diese automatische deterministische Finalisierungsstrategie stellt sicher, dass die in Ihrem Code verwendeten nicht verwalteten Ressourcen bereinigt werden.

Sie sollten IDisposable für jeden Typ implementieren, der über einen Finalizer verfügt. Es wird empfohlen, sowohl Dispose als auch Finalize zu implementieren, wenn Ihre Klasse nicht verwaltete Ressourcen enthält.

Das folgende Codeausschnitt zeigt, wie Sie das Dispose Finalize-Muster in C # implementieren können.

geschützte virtuelle Leere Entsorgen (Bool Disposing)

        {

            if (entsorgen)

            {

                // Code schreiben, um verwaltete Objekte zu bereinigen

            }}

            // Code schreiben, um nicht verwaltete Objekte und Ressourcen zu bereinigen

        }}

Diese parametrisierte Dispose-Methode kann automatisch vom Destruktor aufgerufen werden, wie im folgenden Codeausschnitt gezeigt.

  ~ Ressourcen ()

        {

            if (! entsorgt)

            {

                entsorgt = wahr;

                Entsorgen (falsch);

            }}

        }}