O que você está perdendo aqui é que o compilador está estendendo a vida útil da sua x
variável até o final do método em que está definida - isso é apenas algo que o compilador faz - mas apenas o faz para uma compilação DEBUG.
Se você alterar o código para que a variável seja definida em um método separado, ela funcionará conforme o esperado.
A saída do seguinte código é:
False
True
E o código:
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();
}
}
}
Então, basicamente, seu entendimento estava correto, mas você não sabia que o compilador furtivo manteria sua variável ativa até depois que você ligou GC.Collect()
- mesmo que você a defina explicitamente como nula!
Como observei acima, isso só acontece para uma compilação DEBUG - presumivelmente para que você possa inspecionar os valores para variáveis locais enquanto depura até o final do método (mas isso é apenas um palpite!).
O código original funciona como esperado para uma compilação de lançamento - portanto, o código a seguir gera false, true
uma compilação RELEASE e false, false
uma compilação 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.
}
}
}
Como um adendo: observe que, se você fizer algo no finalizador de uma classe que faça com que uma referência ao objeto que está sendo finalizado seja alcançável a partir de uma raiz do programa, esse objeto NÃO será coletado como lixo, a menos e até que esse objeto não esteja mais referenciado.
Em outras palavras, você pode dar a um objeto uma "suspensão da execução" através do finalizador. Geralmente, isso é considerado um projeto ruim!
Por exemplo, no código acima, onde fazemos _extendMyLifetime = this
no finalizador, estamos criando uma nova referência ao objeto, para que ele não seja coletado como lixo até que _extendMyLifetime
(e qualquer outra referência) não faça mais referência a ele.
Person1
? Eu vejo apenasPerson
. Por último: consulte docs.microsoft.com/dotnet/csharp/programming-guide/… para saber como os finalizadores funcionam.