Почему код генерирует вызываемый класс MSIL <>c__DisplayClass1
У меня есть этот код,
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;
}
Почему это создает вложенный класс с именем <>c__DisplayClass1 в 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
Сгенерированный код мешает анализу кода Nitriq, поэтому я хочу понять, почему он существует.
2 ответа
Если вы используете локальные переменные в лямбде, это должно быть в куче. Лямбда может использоваться после выхода из создавшей ее функции. Нормальные локальные переменные (находящиеся в стеке / регистрах) становятся недействительными при выходе из функции, поэтому их нельзя использовать здесь.
Таким образом, компилятор C# создает класс для хранения захваченных локальных переменных. Это тот, кого вы видите.
Обратите внимание, что C# фиксирует фактическую переменную, а не ее текущее значение. Так что концептуально это запечатлено по ссылке. Семантика захвата означает, что компилятор должен создать один контейнерный объект для каждой области.
http://csharpindepth.com/Articles/Chapter5/Closures.aspx
В вашем коде
x =>
{
return x.StartMetres == startMetres && x.EndMetres == divisionPosition;
}
Лямбда использует startMetres
а также divisionPosition
так что оба они попадают в плен и помещаются в этот вложенный класс.
Вы используете лямбду для метода расширения Where, который требует, чтобы компилятор генерировал класс всякий раз, когда лямбда захватывает внешние переменные. В этом случае оба startMetres
а также divisionPosition
параметры фиксируются.
Вы видите класс, который генерирует компилятор для хранения захваченных переменных.