С учетом следующего кода:
public class C
{
public void M()
{
var x = 5;
Action<int> action = y => Console.WriteLine(y);
}
}
Использование VS2013,.NET 4.5. Когда мы смотрим на декомпилированный код, мы видим, что компилятор кэширует делегат на сайте вызова:
public class C
{
[CompilerGenerated]
private static Action<int> CS$<>9__CachedAnonymousMethodDelegate1;
public void M()
{
if (C.CS$<>9__CachedAnonymousMethodDelegate1 == null)
{
C.CS$<>9__CachedAnonymousMethodDelegate1 = new Action<int>(C.<M>b__0);
}
Action<int> arg_1D_0 = C.CS$<>9__CachedAnonymousMethodDelegate1;
}
[CompilerGenerated]
private static void <M>b__0(int y)
{
Console.WriteLine(y);
}
}
Глядя на тот же код, декомпилированный в Roslyn (используя TryRoslyn), выдается следующий вывод:
public class C
{
[CompilerGenerated]
private sealed class <>c__DisplayClass0
{
public static readonly C.<>c__DisplayClass0 CS$<>9__inst;
public static Action<int> 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 <M>b__1(int y)
{
Console.WriteLine(y);
}
}
public void M()
{
Action<int> arg_22_0;
if (arg_22_0 = C.<>c__DisplayClass0.CS$<>9__CachedAnonymousMethodDelegate2 == null)
{
C.<>c__DisplayClass0.CS$<>9__CachedAnonymousMethodDelegate2 =
new Action<int>(C.<>c__DisplayClass0.CS$<>9__inst.<M>b__1);
}
}
}
Теперь мы можем видеть, что делегат теперь переносится в закрытый класс внутри C
, подобное поведение, которое мы привыкли видеть при закрытии переменной экземпляра/поля (закрытие).
Я знаю, что это деталь реализации, которая может быть изменена в любой момент времени.
И все-таки интересно, какие преимущества отнимают делегат в новый класс и кэшируют его там, просто кешируя его на сайте вызова?
Edit:
Этот вопрос говорит о том же поведении, что и здесь.