我似乎遇到了 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 关闭相同的变量,这也会使代码生成变得更加危险和复杂。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)