Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Does Code Generate MSIL Class Called <>c__DisplayClass1

Tags:

c#

.net

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.

like image 973
peter Avatar asked May 16 '11 04:05

peter


2 Answers

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.

like image 179
CodesInChaos Avatar answered Sep 22 '22 13:09

CodesInChaos


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.

like image 21
dlev Avatar answered Sep 19 '22 13:09

dlev