Given the following code:
public class C { public void M() { var x = 5; Action action = y => Console.WriteLine(y); } } Using VS2013, .NET 4.5. When looking at the decompiled code, we can see that the compiler is caching the delegate at the call site:
public class C { [CompilerGenerated] private static Action CS$9__CachedAnonymousMethodDelegate1; public void M() { if (C.CS$9__CachedAnonymousMethodDelegate1 == null) { C.CS$9__CachedAnonymousMethodDelegate1 = new Action(C.b__0); } Action arg_1D_0 = C.CS$9__CachedAnonymousMethodDelegate1; } [CompilerGenerated] private static void b__0(int y) { Console.WriteLine(y); } } Looking at the same code decompiled in Roslyn (using TryRoslyn), yields the following output:
public class C { [CompilerGenerated] private sealed class c__DisplayClass0 { public static readonly C.c__DisplayClass0 CS$9__inst; public static Action CS$9__CachedAnonymousMethodDelegate2; static c__DisplayClass0() { // Note: this type is marked as 'beforefieldinit'. C.c__DisplayClass0.CS$9__inst = new C.c__DisplayClass0(); } internal void b__1(int y) { Console.WriteLine(y); } } public void M() { Action arg_22_0; if (arg_22_0 = C.c__DisplayClass0.CS$9__CachedAnonymousMethodDelegate2 == null) { C.c__DisplayClass0.CS$9__CachedAnonymousMethodDelegate2 = new Action(C.c__DisplayClass0.CS$9__inst.b__1); } } } We can now see that the delegate is now lifted into a private class inside C, a similar behavior that we're used to seeing when closing over an instance variable / field (closure).
I know this is an implementation detail which may be subject to change at any given time.
Still I wonder, what are the benefits of lifting the delegate into a new class and caching it there over simply caching it at the call site?
Edit:
This issue talks about the same behavior as asked here.