Что означает этот перечислитель, сгенерированный компилятором?
Я написал довольно сложный метод, который дает доходность IEnumerable<string>
, но когда я проверял выходные данные компилятора в Reflector, я не понимал конкретную часть сгенерированной компилятором реализации IEnumerator
:
void IDisposable.Dispose()
{
switch (this.<>1__state)
{
case 1:
case 2:
case 3:
switch (this.<>1__state) // empty switch! why?!
{
}
break;
default:
return;
try // What?! AFTER return?!
{
}
finally // is the try-finally block anyhow relevant?
{
this.<>m__Finallya();
}
break;
}
this.<>m__Finally7();
}
Я предполагаю (или надеюсь), что Reflector не поместил закрывающую скобку внешнего switch
и что это должно быть непосредственно после return
, Тем не менее, я не понимаю, почему в случае 3 есть пустой переключатель, или почему m__Finallya
вызывается в finally
блок. (Есть ли смысловая разница между работой в обычном режиме и внутри finally
блок? Кроме CER, которых у меня нет в моем коде.)
Для справки, вот IL:
.method private hidebysig newslot virtual final
instance void System.IDisposable.Dispose() cil managed
{
.override [mscorlib]System.IDisposable::Dispose
// Code size 69 (0x45)
.maxstack 2
.locals init ([0] int32 CS$0$0000,
[1] int32 CS$0$0001)
IL_0000: ldarg.0
IL_0001: ldfld int32 FBD.TIP.Reader.MissingMessagesReader/'<GetMissingMessages>d__0'::'<>1__state'
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldc.i4.1
IL_0009: sub
IL_000a: switch (
IL_001c,
IL_001c,
IL_001c)
IL_001b: ret
IL_001c: ldarg.0
IL_001d: ldfld int32 FBD.TIP.Reader.MissingMessagesReader/'<GetMissingMessages>d__0'::'<>1__state'
IL_0022: stloc.1
IL_0023: ldloc.1
IL_0024: ldc.i4.2
IL_0025: sub
IL_0026: switch (
IL_0035,
IL_0035)
IL_0033: br.s IL_003e
.try
{
IL_0035: leave.s IL_003e
} // end .try
finally
{
IL_0037: ldarg.0
IL_0038: call instance void FBD.TIP.Reader.MissingMessagesReader/'<GetMissingMessages>d__0'::'<>m__Finallya'()
IL_003d: endfinally
} // end handler
IL_003e: ldarg.0
IL_003f: call instance void FBD.TIP.Reader.MissingMessagesReader/'<GetMissingMessages>d__0'::'<>m__Finally7'()
IL_0044: ret
} // end of method '<GetMissingMessages>d__0'::System.IDisposable.Dispose
3 ответа
Это просто рефлектор, пытающийся не отставать от сгенерированного IL (поскольку блоки итераторов не должны относиться к "нормальному" C#, пока они являются действительными IL). В частности, ret
после finally
блок.
Вы не показали, как выглядит ваш оригинальный блок итератора, но мой опыт работы с Reflector и сгенерированным компилятором кодом заключается в том, что ему не всегда удается полностью точно декомпилировать, потому что компилятор использует некоторый IL, который не имеет эквивалента в C#.
У меня есть статья о реализации блока итераторов, которая может вам немного помочь, но я бы не стал сильно беспокоиться о том, как выглядит скомпилированный код. В некоторых случаях компилятор C# почти наверняка генерирует ненужный код на том основании, что это упрощает компилятор. Блоки итераторов, должно быть, были очень хитрыми, чтобы получить правильные (они могут стать очень сложными, с наконец-то блоками и удалением итераторов), поэтому я думаю, что разумно просто довериться JIT, чтобы оптимизировать ненужные биты, такие как switch/case в вашем сгенерированном коде.
Я мог бы утверждать, что компилятор C# глуп, (это, вероятно, немного глупо). Также вполне возможно, что этот код выглядит совсем по-другому, когда подключается во время выполнения (весь этот неприятный мусор опускается).
Во всяком случае, вы, возможно, знакомы с конечными автоматами? Когда вы пишете генераторы на C# (yield stuff), вы говорите компилятору испускать анонимный тип, который реализует этот генератор как конечный автомат. Это хороший формальный подход, который должен быть проверяемым. Это, вероятно, причины, почему это выглядит так, как это происходит.