微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

C#闭包堆分配在方法开始时发生

我似乎遇到了C#编译器的一些奇怪行为.

请考虑以下代码示例:

static void Main(string[] args)
{
    Foo(false,8);
}

public static void Foo(bool execute,int x)
{
    if (execute)
    {
        Task.Run(() => Console.WriteLine(x));
    }
}

运行此(在发布中)显示发生了一些意外的分配.检查IL表明闭包触发的堆分配出现在函数的最开头,而不是在条件内:

.method public hidebysig static void 
    Foo(
      bool execute,int32 x
    ) cil managed 
  {
    .maxstack 2
    .locals init (
      [0] class Test.Program/'<>c__displayClass1_0' 'CS$<>8__locals0'
    )

    IL_0000: newobj       instance void Test.Program/'<>c__displayClass1_0'::.ctor()
    IL_0005: stloc.0      // 'CS$<>8__locals0'
    IL_0006: ldloc.0      // 'CS$<>8__locals0'
    IL_0007: ldarg.1      // x
    IL_0008: stfld        int32 Test.Program/'<>c__displayClass1_0'::x

    // [18 13 - 18 25]
    IL_000d: ldarg.0      // execute
    IL_000e: brfalse.s    IL_0022

    // [20 17 - 20 54]
    IL_0010: ldloc.0      // 'CS$<>8__locals0'
    IL_0011: ldftn        instance void Test.Program/'<>c__displayClass1_0'::'<Foo>b__0'()
    IL_0017: newobj       instance void [mscorlib]System.Action::.ctor(object,native int)
    IL_001c: call         class [mscorlib]System.Threading.Tasks.Task [mscorlib]System.Threading.Tasks.Task::Run(class [mscorlib]System.Action)
    IL_0021: pop          

    // [22 9 - 22 10]
    IL_0022: ret          

  } // end of method Program::Foo

在这里遗漏了什么,是否有人对这种奇怪的行为有解释? Roslyn是否可能生成为闭包分配的代码,无论我们是否实际执行它们?

解决方法

此行为是设计使然.

当你的方法一个闭包时,闭包内使用的所有变量都必须是闭包类的一部分(这样lambda就可以访问它们的当前值).

如果编译器没有立即分配闭包,则在创建闭包实例时,必须将值从局部变量复制到闭包类的字段,浪费时间和内存.

如果具有不同可达性(或者更糟的是嵌套范围)的多个lambda靠近相同的变量,那么这也会使codegen风险更大,更复杂.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐