Consider the following code:
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);
}
}
With the following output:
a.IsAlive: False
a.Target:
And here's:ConsoleApp.A
Why is it false and null? A hasn’t been collected yet.
EDIT: Oh ye of little faith.
I added the following lines:
Console.WriteLine("And here's:" + a);
GC.KeepAlive(a);
See the updated output.
Updated answer for updated question.
With the new question, we go through the following steps.
(If B was finalised at point 4 it would be possible for it to be collected before point 5, as while B awaiting finalisation keeps both B and B.a from collection, B.a awaiting finalisation does not affect B’s collection).
What has happened is that the order between 4 and 5 was such that B.a was finalised and then B was finalised. Since the reference a WeakReference holds to an object is not a normal reference, it needs its own clean-up code to release its GCHandle. Obviously it can’t depend on normal GC collection behaviour, since the whole point of its references is that they don’t follow normal GC collection behaviour.
Now B’s finaliser is run, but since the behaviour of B.a’s finaliser was to release its reference it returns false for IsAlive (or in .NET prior to 1.1 if I’m remembering the versions right, throws an error).