为什么代码生成称为 c__DisplayClass1的MSIL类

我有这个代码,

private bool MatchingBreak(IEnumerable breaks, int startMetres, int divisionPosition) { CarriagewaySummary matchingBreak = breaks.Where(x => { return x.StartMetres == startMetres && x.EndMetres == divisionPosition; }).SingleOrDefault(); return matchingBreak != null; } 

为什么在MSIL中生成一个名为 c__DisplayClass1的嵌套类?

 .class nested private auto ansi sealed beforefieldinit c__DisplayClass1 extends object { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Fields .field public int32 startMetres .field public int32 divisionPosition // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x56fb // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void object::.ctor() IL_0006: ret } // End of method c__DisplayClass1..ctor .method public hidebysig instance bool b__0 ( class TreatmentLengthDynamicSegmentation.Domain.CarriagewaySummary x ) cil managed { // Method begins at RVA 0x5704 // Code size 37 (0x25) .maxstack 2 .locals init ( [0] bool ) IL_0000: nop IL_0001: ldarg.1 IL_0002: callvirt instance int32 TreatmentLengthDynamicSegmentation.Domain.CarriagewaySummary::get_StartMetres() IL_0007: ldarg.0 IL_0008: ldfld int32 class TreatmentLengthDynamicSegmentation.ScriptHelpers.DivisionManager/c__DisplayClass1::startMetres IL_000d: bne.un.s IL_001f IL_000f: ldarg.1 IL_0010: callvirt instance int32 TreatmentLengthDynamicSegmentation.Domain.CarriagewaySummary::get_EndMetres() IL_0015: ldarg.0 IL_0016: ldfld int32 class TreatmentLengthDynamicSegmentation.ScriptHelpers.DivisionManager/c__DisplayClass1::divisionPosition IL_001b: ceq IL_001d: br.s IL_0020 IL_001f: ldc.i4.0 IL_0020: stloc.0 IL_0021: br.s IL_0023 IL_0023: ldloc.0 IL_0024: ret } // End of method c__DisplayClass1.b__0 } // End of class TreatmentLengthDynamicSegmentation.ScriptHelpers.DivisionManager/c__DisplayClass1 

生成的代码干扰了Nitriq代码分析,所以我想了解它为什么存在。

如果在lambda中使用局部变量,则需要在堆上。 lambda可能在创建它的函数退出后使用。 当函数退出时,正常的局部变量(存在于堆栈/寄存器中)变为无效,因此不能在此处使用它们。

因此C#编译器创建一个类来保存捕获的局部变量。 那就是你所看到的那个。

请注意,C#捕获实际变量,而不是其当前值。 所以在概念上它是通过引用捕获的。 捕获的语义意味着编译器需要为每个作用域创建一个容器对象。

http://csharpindepth.com/Articles/Chapter5/Closures.aspx


在你的代码中

 x => { return x.StartMetres == startMetres && x.EndMetres == divisionPosition; } 

lambda使用startMetresdivisionPosition ,因此它们都被捕获并放入该嵌套类中。

你正在使用lambda作为Where扩展方法,这需要编译器在lambda捕获外部变量时生成一个类。 在这种情况下,捕获startMetresdivisionPosition参数。

您正在查看编译器生成的类以保存捕获的变量。