Was bedeutet dieser Compiler generierte enumerator bedeuten?
-
23-08-2019 - |
Frage
Ich schrieb eine ziemlich komplexe Methode, die Ausbeute-returns IEnumerable<string>
, aber wenn ich die Compilerausgabe in Reflector inspizierte, ich verstand nicht, einen bestimmten Teil der Compiler generierten Implementierung von 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();
}
Ich vermute (oder gehofft), dass Reflector die schließende Klammer des äußeren switch
verlegt wird, und dass sie direkt nach dem return
sein. Trotzdem verstehe ich nicht, warum es ein leerer Schalter in Fall 3, oder warum m__Finallya
in einem finally
Block aufgerufen wird. (Gibt es einen semantischen Unterschied zwischen normal läuft und in einem finally
Block? Anders' als CERs, die ich nicht habe in meinem Code.)
Als Referenz ist hier die 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
Lösung
Das ist einfach zu kämpfen Reflektor mit dem IL Schritt zu halten, die erzeugt wurde (seit Iterator Blöcke müssen so lange zu „normalen“ C # nicht betreffen, da sie IL gelten). Insbesondere ist die ret
nach dem finally
Block.
Andere Tipps
Sie haben nicht gezeigt, was Ihre ursprüngliche Iteratorblock aussieht, aber meine Erfahrung von Reflektor und Compiler generierte Code ist, dass es nicht immer gelingt ganz genau zu dekompilieren, weil der Compiler einige IL verwendet, die nicht existiert ein Äquivalent in C #.
Ich habe einen Artikel über Iteratorblock Implementierung habe, die Ihnen ein wenig helfen können , aber ich würde nicht zu viel Sorgen darüber, was der kompilierte Code aussieht. In einigen Fällen wird die C # -Compiler Erzeugung an Sicherheit grenzender Wahrscheinlichkeit unnötigen Code mit der Begründung, das hält den Compiler einfacher. Iterator Blöcke muss sehr heikel gewesen richtig zu machen (es kann sehr kompliziert werden, mit schließlich Blöcken und Iterator zur Verfügung), so halte ich es für sinnvoll ist nur der JIT zu vertrauen, die unnötigen Bits wie der Schalter / Fall in Ihrem generierten Code zu optimieren entfernt.
Ich könnte argumentieren, dass der C # -Compiler ist dumm, (es ist wahrscheinlich ein bisschen dumm ist). Es ist auch durchaus möglich, dass dieser Code ganz anders aussieht, wenn sie von der Laufzeit (alles, was böser Müll weggelassen wird).
jittedWie auch immer, Sie vielleicht mit State-Maschinen vertraut? Wenn Sie Generatoren in C # (Ausbeute Zeug) schreiben Sie den Compiler sagen einen anonymen Typ zu emittieren, die diesen Generator als eine Zustandsmaschine implementiert. Dies ist ein schöner formaler Ansatz, der nachprüfbar sein sollte. Das ist wahrscheinlich der Grund, warum es so aussieht, es funktioniert.