Brakuje tutaj tego, że kompilator wydłuża czas życia x
zmiennej aż do końca metody, w której jest zdefiniowany - to jest po prostu kompilator - ale robi to tylko dla kompilacji DEBUG.
Jeśli zmienisz kod, tak aby zmienna została zdefiniowana w oddzielnej metodzie, będzie działać zgodnie z oczekiwaniami.
Dane wyjściowe następującego kodu to:
False
True
I kod:
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();
}
}
}
Więc w zasadzie zrozumienie było poprawne, ale nie wiedziałem, że podstępne kompilator zamiar utrzymać przy życiu dopóki zmienna po zadzwoniłeś GC.Collect()
- nawet jeśli jawnie ustawić go na null!
Jak zauważyłem powyżej, dzieje się tak tylko w przypadku kompilacji DEBUG - przypuszczalnie po to, aby sprawdzić wartości zmiennych lokalnych podczas debugowania do końca metody (ale to tylko przypuszczenie!).
Oryginalny kod NIE działa zgodnie z oczekiwaniami w przypadku kompilacji wydania - więc następujące dane wyjściowe false, true
dla kompilacji RELEASE i false, false
kompilacji DEBUG:
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.
}
}
}
Jako uzupełnienie: Zauważ, że jeśli zrobisz coś w finalizatorze dla klasy, która powoduje, że odwołanie do finalizowanego obiektu jest osiągalne z katalogu głównego programu, wówczas ten obiekt NIE zostanie wyrzucony, dopóki i ten obiekt już nie będzie o którym mowa.
Innymi słowy, można nadać obiektowi „wstrzymanie wykonania” za pośrednictwem finalizatora. Jest to jednak ogólnie uważane za zły projekt!
Na przykład w powyższym kodzie, w którym robimy to _extendMyLifetime = this
w finalizatorze, tworzymy nowe odwołanie do obiektu, więc nie będzie ono teraz zbierane w pamięci, dopóki _extendMyLifetime
(i wszelkie inne odniesienia) nie będą już do niego odwoływać.
Person1
? Widzę tylkoPerson
. Na koniec : zobacz docs.microsoft.com/dotnet/csharp/programming-guide/…, aby dowiedzieć się, jak działają finalizatory.