I have this code,
private bool MatchingBreak(IEnumerable<CarriagewaySummary> breaks, int startMetres, int divisionPosition)
{
CarriagewaySummary matchingBreak = breaks.Where(x =>
{
return x.StartMetres == startMetres && x.EndMetres == divisionPosition;
}).SingleOrDefault();
return matchingBreak != null;
}
Why does that generate a nested class called <>c__DisplayClass1 in the MSIL?
.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 <MatchingBreak>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.<MatchingBreak>b__0
} // End of class TreatmentLengthDynamicSegmentation.ScriptHelpers.DivisionManager/<>c__DisplayClass1
The generated code is interfering with Nitriq Code Analysis, so I want to understand why it's there.
If you use local variables in a lambda it needs to be on the heap. The lambda might be used after the function which created it exits. Normal local variables (living on the stack/registers) become invalid when the function exits, so they can't be used here.
So the C# compiler creates a class to hold captured local variables. That's the one you're seeing.
Note that C# captures the actual variable, not its current value. So conceptually it's captured by reference. The semantics of capturing mean that the compiler needs to create one container object per scope.
http://csharpindepth.com/Articles/Chapter5/Closures.aspx
In your code
x =>
{
return x.StartMetres == startMetres && x.EndMetres == divisionPosition;
}
The lambda uses startMetres
and divisionPosition
, so both of them get captured and are put in that nested class.
You're using a lambda for the Where extension method, which requires the compiler to generate a class whenever the lambda captures outer variables. In this case both the startMetres
and divisionPosition
parameters are captured.
You're seeing the class the compiler generates in order to hold the captured variables.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With