ここで欠けているのは、コンパイラーがx
変数のライフタイムを、それが定義されているメソッドの最後まで延長していることです。これは、コンパイラーが行うことの1つですが、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 /…を参照してください。