O que isso significa recenseador gerado pelo compilador?
-
23-08-2019 - |
Pergunta
Eu escrevi um método bastante complexo que rendimento retornos IEnumerable<string>
, mas quando eu inspecionou a saída do compilador no refletor, eu não entendi uma parte específica da implementação gerado pelo compilador de 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();
}
Eu estou supondo (ou esperando) que refletor perdido a chave de fechamento do switch
exterior, e que ele deve ser, imediatamente após a return
. Ainda assim, eu não entendo porque é que há um interruptor vazio no caso 3, ou por m__Finallya
está sendo chamado em um bloco finally
. (Existe uma diferença semântica entre funcionando normalmente e dentro de um bloco finally
? Além CER de que eu não tenho no meu código.)
Para referência, aqui é a 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
Solução
Isso é simplesmente refletor lutando para manter-se com o IL que foi gerado (desde iterador blocos não têm de se relacionar com "normal" C #, enquanto eles estão IL válido). Em particular, o ret
é após o bloco finally
.
Outras dicas
Você não tem mostrado o que a sua aparência bloco iterador originais como, mas minha experiência de refletor e um código gerado pelo compilador é que nem sempre conseguem decompor totalmente com precisão, porque o compilador usa algum IL que não tem um equivalente em C #.
Eu tenho um href="http://csharpindepth.com/Articles/Chapter11/StreamingAndIterators.aspx" artigo sobre iterador bloco implementação que pode ajudá-lo um pouco , mas eu não me preocuparia muito com o que os olhares código compilado como. Em alguns casos, o compilador C # é quase certamente gerar o código desnecessário, alegando que que mantém o compilador simples. Iterator blocos deve ter sido muito complicado para obter direito (ele pode ficar muito complicado, com finally blocos e iterator disposição) Então eu acho que é razoável apenas confiar no JIT para otimizar afastado os bits desnecessários como o switch / case no seu código gerado.
Eu poderia argumentar que o compilador C # é estúpido, (que provavelmente é um pouco estúpido). Também é bem possível que isso parece código muito diferentes quando JITted pelo run-time (todo esse lixo desagradável se omitido).
De qualquer forma, você talvez familiarizado com máquinas de estado? Quando você escreve geradores em C # (coisas de rendimento) você diz o compilador para emitir um tipo anônimo que implementa este gerador como uma máquina de estado. Esta é uma abordagem formal agradável que se destina a ser verificáveis. Isso é provavelmente as razões pelas quais a aparência que ele faz.