Strange effect of C# closures on garbage collector

Straight to the code

class Program
{
    private static WeakReference<EventHandler<EventArgs>> _noClosure;
    private static WeakReference<EventHandler<EventArgs>> _closure;

    static void Main(string[] args)
    {
        Init();
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        EventHandler<EventArgs> target;
        Console.WriteLine(_closure.TryGetTarget(out target));
        Console.WriteLine(_noClosure.TryGetTarget(out target));
    }

    class C { public void Go() { } }

    private static void Init()
    {
        _noClosure = new WeakReference<EventHandler<EventArgs>>((sender, args) =>
        {
        });

        var s = new C();
        _closure = new WeakReference<EventHandler<EventArgs>>((sender, args) =>
        {
              s.Go();
        });
    }
}

The output I get from this code is

False
True

How on earth is this possible?

P.S. I came into this while trying to figure out how WeakEventManager works.

Jon Skeet
people
quotationmark

The compiler will cache the _noClosure delegate in a static field, and reuse it every time you call Init. The exact same instance can be reused every time.

Compare that with _closure, which closes over a new instance of C() on each call to Init() - that can't be cached.

The caching of _noClosure means that there's a strong reference (the field) to the delegate, so it can't be garbage collected.

If you run ildasm on your application, you can see all of this in action.

people

See more on this question at Stackoverflow