La chose qui vous manque ici est que le compilateur prolonge la durée de vie de votre x
variable jusqu'à la fin de la méthode dans laquelle elle est définie - c'est juste quelque chose que le compilateur fait - mais il ne le fait que pour une construction DEBUG.
Si vous modifiez le code afin que la variable soit définie dans une méthode distincte, cela fonctionnera comme prévu.
La sortie du code suivant est:
False
True
Et le 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();
}
}
}
Donc, fondamentalement, votre compréhension était correcte, mais vous ne saviez pas que le compilateur sournois allait garder votre variable en vie jusqu'à ce que vous l'appeliez GC.Collect()
- même si vous la définissez explicitement sur null!
Comme je l'ai noté ci-dessus, cela ne se produit que pour une build DEBUG - probablement pour que vous puissiez inspecter les valeurs des variables locales pendant le débogage jusqu'à la fin de la méthode (mais c'est juste une supposition!).
Le code d'origine FONCTIONNE comme prévu pour une build de version - donc le code suivant sort false, true
pour une build RELEASE et false, false
pour une build 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.
}
}
}
En tant qu'additif: notez que si vous faites quelque chose dans le finaliseur pour une classe qui fait qu'une référence à l'objet en cours de finalisation est accessible à partir d'une racine de programme, alors cet objet ne sera PAS récupéré à moins que et jusqu'à ce que cet objet ne soit plus référencé.
En d'autres termes, vous pouvez donner à un objet un "sursis d'exécution" via le finaliseur. Ceci est généralement considéré comme une mauvaise conception!
Par exemple, dans le code ci-dessus, où nous le faisons _extendMyLifetime = this
dans le finaliseur, nous créons une nouvelle référence à l'objet, il ne sera donc plus récupéré tant que _extendMyLifetime
(et toute autre référence) ne le référencera plus.
Person1
? Je vois seulementPerson
. Dernier: voir docs.microsoft.com/dotnet/csharp/programming-guide/… pour le fonctionnement des finaliseurs.