是否将匿名lambda转换为强类型的委托会禁用编译器缓存?

Laz*_*zlo 4 c# lambda delegates

我试图了解编译器支持的委托缓存的一种极端情况,以避免内存分配。

例如,据我所知,此委托被缓存到单个实例中并被重用,因为它没有关闭任何局部变量:

int[] set = new [] { 1, 2, 3, 4, 5, 6 };
var subset = set.Where(x => x % 2 == 0);
Run Code Online (Sandbox Code Playgroud)

现在,在某些情况下,生成的代码可能想直接调用委托,因此匿名方法不是有效的C#,例如:

var result = (x => x % 2 == 0).Invoke(5); // Invalid
Run Code Online (Sandbox Code Playgroud)

为了避免这种情况,我看到两个选择:

  1. 使用构造函数:
var result = (new Func<int, bool>(x => x % 2 == 0)).Invoke(5);
Run Code Online (Sandbox Code Playgroud)
  1. 投射匿名委托:
var result = ((Func<int, bool>)(x => x % 2 == 0)).Invoke(5);
Run Code Online (Sandbox Code Playgroud)

我假设编译器不会在选项#1中缓存该委托,但是我不确定它是否将在#2中。

这在任何地方都有记录吗?

Jon*_*eet 9

我假设编译器不会在选项#1中缓存该委托,但是我不确定它是否将在#2中。

实际上,在两种情况下都可以,并且它们捆绑在一起。

根据ECMA C#5规范的第7.6.10.5节:

new D(E)形式的委托创建表达式的绑定时处理,其中D为委托类型,E为表达式,包括以下步骤:

  • ...
  • 如果E是匿名函数,则委托创建表达式的处理方式与从E到D的匿名函数转换(第6.5节)相同。
  • ...

因此,基本上,这两种处理方式相同。并且在两种情况下都可以缓存。是的,“新不一定意味着新”是很奇怪的。

为了说明这一点,让我们编写一个简单的程序:

using System;

public class Program
{
    public static void Main()
    {
        var func = new Func<int, bool>(x => x % 2 == 0);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是Main我的机器上方法的IL (可以使用C#8预览编译器进行构建,但我希望在一段时间内可以看到相同的结果):

.method public hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       29 (0x1d)
  .maxstack  8
  IL_0000:  ldsfld     class [mscorlib]System.Func`2<int32,bool> Program/'<>c'::'<>9__0_0'
  IL_0005:  brtrue.s   IL_001c
  IL_0007:  ldsfld     class Program/'<>c' Program/'<>c'::'<>9'
  IL_000c:  ldftn      instance bool Program/'<>c'::'<Main>b__0_0'(int32)
  IL_0012:  newobj     instance void class [mscorlib]System.Func`2<int32,bool>::.ctor(object,
                                                                                      native int)
  IL_0017:  stsfld     class [mscorlib]System.Func`2<int32,bool> Program/'<>c'::'<>9__0_0'
  IL_001c:  ret
} // end of method Program::Main
Run Code Online (Sandbox Code Playgroud)

这实际上是:

Func<int, bool> func;
func = cache;
if (func == null)
{
    func = new Func<int, bool>(GeneratedPrivateMethod);
    cache = func;
}
Run Code Online (Sandbox Code Playgroud)