Che cosa significa questo enumeratore generato dal compilatore?
-
23-08-2019 - |
Domanda
Ho scritto un metodo abbastanza complicato cedere i ritorni IEnumerable<string>
, ma quando ho controllato l'output del compilatore in Reflector, non capivo una parte specifica per l'esecuzione generato dal compilatore di 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();
}
Sto indovinando (o sperare) che riflettore fuori luogo la coppia del switch
esterna chiusura, e che dovrebbe essere direttamente dopo il return
. Ancora, non capisco perché c'è un interruttore vuoto nel caso 3, o perché m__Finallya
viene richiamato in un blocco finally
. (C'è una differenza semantica tra l'esecuzione normalmente e all'interno di un blocco finally
? Altro che CER, che non ho nel mio codice.)
Per avere un riferimento, ecco la 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
Soluzione
che è semplicemente riflettore lottando per tenere il passo con l'IL che è stato generato (dal blocchi iteratore non devono riguardare "normale" C #, purché siano validi IL). In particolare, il ret
è dopo il blocco finally
.
Altri suggerimenti
Non hai mostrato ciò che il vostro blocco iteratore originale sarà simile, ma la mia esperienza di riflettore e il codice generato dal compilatore è che non sempre riesce a decompilare tutto con precisione, perché il compilatore utilizza alcuni IL che non ha un equivalente in C #.
Ho un articolo su blocco iteratore implementazione che può aiutare un po ' , ma io non mi preoccuperei troppo di ciò che il codice compilato assomiglia. In alcuni casi il compilatore C # è quasi certamente genera codice non necessario per il fatto che che mantiene il compilatore più semplice. blocchi iteratore deve essere stato molto difficile da ottenere (si può ottenere molto complicato, con blocchi finally e lo smaltimento iterator) quindi penso che sia ragionevole fidarsi solo il JIT per ottimizzare via i bit inutili come l'opzione / caso nel codice generato.
ho potuto sostenere che il compilatore C # è stupido, (è probabilmente è un po 'stupido). E 'anche possibile che questo codice sembra molto diverso quando jitted dal run-time (tutto quel brutto spazzatura viene omesso).
In ogni caso, è forse familiarità con macchine a stati? Quando si scrive generatori in C # (roba resa) si dice al compilatore di emettere un tipo anonimo che implementa questo generatore come una macchina a stati. Questo è un bel approccio formale che è destinato ad essere verificabili. Questo è probabilmente i motivi per cui si guarda il modo in cui funziona.