Что означает этот перечислитель, созданный компилятором?

StackOverflow https://stackoverflow.com/questions/555796

Вопрос

Я написал довольно сложный метод, который возвращает доходность 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
Это было полезно?

Решение

Это просто отражатель, изо всех сил пытающийся не отставать от сгенерированного IL (поскольку блоки итератора не обязательно должны относиться к «нормальному» C#, если они являются допустимыми IL).В частности, ret это после finally блокировать.

Другие советы

Вы не показали, как выглядит исходный блок итератора, но мой опыт работы с Reflector и кодом, генерируемым компилятором, показывает, что его не всегда удается полностью точно декомпилировать, поскольку компилятор использует некоторый IL, который не имеет эквивалента в С#.

у меня есть статья о реализации блока итератора что может вам немного помочь, но я бы не стал слишком беспокоиться о том, как выглядит скомпилированный код.В некоторых случаях компилятор C# почти наверняка генерирует ненужный код, поскольку это упрощает компилятор.Блоки итераторов, должно быть, очень сложно получить правильно (это может оказаться очень сложным из-за блоковfinally и удаления итераторов), поэтому я думаю, что разумно просто доверить JIT оптимизацию ненужных битов, таких как переключатель/регистр, в вашем сгенерированном коде.

Я мог бы возразить, что компилятор C# глуп (вероятно, он немного глуп).Также вполне возможно, что этот код будет выглядеть совсем по-другому, если его обработать во время выполнения (весь этот неприятный мусор будет опущен).

В любом случае, возможно, вы знакомы с конечными автоматами?Когда вы пишете генераторы на C# (доходность), вы указываете компилятору создать анонимный тип, который реализует этот генератор как конечный автомат.Это хороший формальный подход, который должен быть проверяемым.Вероятно, именно поэтому он выглядит так, как есть.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top