Was Ihnen hier fehlt, ist, dass der Compiler die Lebensdauer Ihrer x
Variablen bis zum Ende der Methode verlängert, in der sie definiert ist - das ist nur etwas, was der Compiler tut -, aber nur für einen DEBUG-Build.
Wenn Sie den Code so ändern, dass die Variable in einer separaten Methode definiert wird, funktioniert sie wie erwartet.
Die Ausgabe des folgenden Codes lautet:
False
True
Und der Code:
using System;
namespace ConsoleApp1
{
class Finalizable
{
~Finalizable()
{
_extendMyLifetime = this;
}
public static bool LifetimeExtended => _extendMyLifetime != null;
static Finalizable _extendMyLifetime;
}
class Program
{
public static void Main()
{
test();
Console.WriteLine(Finalizable.LifetimeExtended); // False.
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine(Finalizable.LifetimeExtended); // True.
}
static void test()
{
new Finalizable();
}
}
}
Im Grunde war Ihr Verständnis also richtig, aber Sie wussten nicht, dass der hinterhältige Compiler Ihre Variable bis nach Ihrem Aufruf am Leben erhalten würde GC.Collect()
- selbst wenn Sie sie explizit auf null setzen!
Wie oben erwähnt, geschieht dies nur für einen DEBUG-Build - vermutlich, damit Sie die Werte beim Debuggen bis zum Ende der Methode auf lokale Variablen überprüfen können (aber das ist nur eine Vermutung!).
Der ursprüngliche Code funktioniert wie erwartet für einen Release-Build. Daher wird der folgende Code false, true
für einen RELEASE-Build und false, false
einen DEBUG-Build ausgegeben:
using System;
namespace ConsoleApp1
{
class Finalizable
{
~Finalizable()
{
_extendMyLifetime = this;
}
public static bool LifetimeExtended => _extendMyLifetime != null;
static Finalizable _extendMyLifetime;
}
class Program
{
public static void Main()
{
new Finalizable();
Console.WriteLine(Finalizable.LifetimeExtended); // False.
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine(Finalizable.LifetimeExtended); // True iff RELEASE build.
}
}
}
Als Ergänzung: Beachten Sie, dass, wenn Sie im Finalizer etwas für eine Klasse tun, das bewirkt, dass ein Verweis auf das zu finalisierende Objekt von einem Programmstamm aus erreichbar ist, dieses Objekt NICHT durch Müll gesammelt wird, es sei denn und bis dieses Objekt nicht mehr vorhanden ist referenziert.
Mit anderen Worten, Sie können einem Objekt über den Finalizer einen "Ausführungsaufenthalt" geben. Dies wird jedoch allgemein als schlechtes Design angesehen!
Zum Beispiel erstellen wir _extendMyLifetime = this
im obigen Code, wie wir es im Finalizer tun, einen neuen Verweis auf das Objekt, sodass es jetzt erst dann mit Müll gesammelt wird, wenn _extendMyLifetime
(und jeder andere Verweis) nicht mehr darauf verweist.
Person1
? Ich sehe nurPerson
. Zuletzt: Informationen zur Funktionsweise von Finalisierern finden Sie unter docs.microsoft.com/dotnet/csharp/programming-guide/… .