Те, що вам тут не вистачає, - це те, що компілятор продовжує термін служби вашої x
змінної до кінця методу, в якому вона визначена - це лише те, що робить компілятор - але це робиться лише для складання DEBUG.
Якщо ви зміните код так, щоб змінна була визначена окремим методом, вона буде працювати, як ви очікували.
Вихід наступного коду:
False
True
І код:
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();
}
}
}
Тому в основному ваше розуміння було правильно, але ви не знаєте , що підлий компілятор збирається, щоб тримати вашу змінну в живих до тих пір , після того, як того, ви подзвонили GC.Collect()
- навіть якщо ви явно встановите її на нуль!
Як я вже зазначав вище, це відбувається лише для складання DEBUG - імовірно, щоб ви могли перевірити значення для локальних змінних під час налагодження до кінця методу (але це лише здогадка!).
Оригінальний код працює, як очікувалося, для збірки релізу - тому наступний код виводить false, true
для збірки RELEASE та false, false
для збірки 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.
}
}
}
Як додаток: Зауважте, що якщо ви робите щось у фіналізаторі для класу, що спричиняє посилання на об'єкт, який завершується, бути доступним з кореня програми, то цей об’єкт НЕ буде збирати сміття, якщо тільки до цього об'єкта більше не буде посилання.
Іншими словами, ви можете дати об'єкту "зупинити виконання" через фіналізатор. Це, як правило, вважається поганим дизайном!
Наприклад, у наведеному вище коді, де ми робимо _extendMyLifetime = this
у фіналізаторі, ми створюємо нове посилання на об’єкт, тому він тепер не буде збирати сміття, поки _extendMyLifetime
(і будь-яка інша посилання) більше не посилається на нього.
Person1
? Я бачу лишеPerson
. Останнє: див. Docs.microsoft.com/dotnet/csharp/programming-guide/… про те, як працюють фіналізатори.