为什么WeakReference在析构函数中无用?

Bub*_*rap 10 c# weak-references

请考虑以下代码:

class Program
{
    static void Main(string[] args)
    {
        A a = new A();
        CreateB(a);

        GC.Collect();
        GC.WaitForPendingFinalizers();
        Console.WriteLine("And here's:" + a);
        GC.KeepAlive(a);
    }

    private static void CreateB(A a)
    {
        B b = new B(a);
    }
}

class A
{ }

class B
{
    private WeakReference a;
    public B(A a)
    {
        this.a = new WeakReference(a);
    }

    ~B()
    {
        Console.WriteLine("a.IsAlive: " + a.IsAlive);
        Console.WriteLine("a.Target: " + a.Target);
    }
}
Run Code Online (Sandbox Code Playgroud)

使用以下输出:

a.IsAlive: False
a.Target:
And here's:ConsoleApp.A
Run Code Online (Sandbox Code Playgroud)

为什么它是假的而且是空的?尚未收到A.

编辑:哦,你们没有信心.

我添加了以下几行:

Console.WriteLine("And here's:" + a);
GC.KeepAlive(a);
Run Code Online (Sandbox Code Playgroud)

查看更新的输出.

Jon*_*nna 3

更新问题的更新答案。

有了新问题,我们将执行以下步骤。

  1. A 和 B 活着并扎根,Ba 活着并通过 B 扎根。
  2. A 活着,B 未生根且符合收集条件。巴没有根和资格。
  3. 收集发生了。B 和 Ba 都是可终结的,因此它们被放入终结器队列中。B 未被收集,因为它是可最终确定的。Ba 未被收集,因为它是可最终确定的,又因为它被尚未最终确定的 B 引用。
  4. 要么Ba最终确定,要么B最终确定。
  5. Ba或B中的另一个已确定。
  6. Ba和B有资格领取。

(如果 B 在第 4 点完成,则有可能在第 5 点之前被收集,因为 B 等待最终确定会使 B 和 Ba 都无法被收集,而 Ba 等待最终确定不会影响 B 的收集)。

所发生的情况是,4 和 5 之间的顺序是 Ba 被最终确定,然后 B 被最终确定。由于 WeakReference 持有的对象引用不是普通引用,因此它需要自己的清理代码来释放其 GCHandle。显然它不能依赖于正常的 GC 收集行为,因为其引用的全部要点是它们不遵循正常的 GC 收集行为。

现在 B 的终结器已运行,但由于 Ba 的终结器的行为是释放其引用,因此它为 IsAlive 返回 false(或者在 1.1 之前的 .NET 中,如果我没记错版本,则会引发错误)。