Question

J'ai écrit une méthode assez complexe rendement rendement IEnumerable<string>, mais quand je visitai la sortie du compilateur dans le réflecteur, je ne comprenais pas une partie spécifique de la mise en œuvre généré par le compilateur 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();
}

Je suppose (ou en espérant) que réflecteur mal placé l'accolade fermante de la switch externe, et qu'il doit être directement après la return. Pourtant, je ne comprends pas pourquoi il y a un interrupteur vide en cas 3, ou pourquoi m__Finallya est appelé dans un bloc de finally. (Y at-il une différence sémantique entre normalement en cours d'exécution et à l'intérieur d'un bloc finally? Autre que CER de, que je n'ai pas dans mon code.)

Pour référence, voici l'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
Était-ce utile?

La solution

Ce réflecteur est tout simplement du mal à suivre l'IL qui a été généré (puisque les blocs itérateur ne doivent pas se rapporter à la « normale » C # aussi longtemps qu'ils sont valables IL). En particulier, le ret est après le bloc finally.

Autres conseils

Vous ne l'avez pas montré ce que votre bloc itérateur d'origine ressemble, mais mon expérience de réflecteur et le code généré par le compilateur est qu'il ne parvient pas toujours à décompiler avec précision tout à fait, parce que le compilateur utilise une IL qui n'a pas un équivalent en C #.

J'ai un article sur l'implémentation bloc itérateur qui peut vous aider un peu , mais je ne vous inquiétez pas trop sur ce qui ressemble le code compilé. Dans certains cas, le compilateur C # est presque certainement générer du code inutile au motif que le compilateur qui maintient plus simple. blocs Iterator doivent avoir été très difficile à obtenir le droit (il peut devenir très compliqué, avec enfin des blocs et l'élimination des iterator) donc je pense qu'il est raisonnable de simplement faire confiance au JIT pour optimiser loin les bits inutiles comme le commutateur / cas dans votre code généré.

Je pourrait dire que le compilateur C # est stupide, (il est probablement un peu stupide). Il est également tout à fait possible que ce code semble très différent quand jitted par le temps d'exécution (toutes ces ordures méchant s'omis).

Quoi qu'il en soit, vous connaissez peut-être avec des machines d'état? Lorsque vous écrivez des générateurs en C # (substance de rendement) vous dites au compilateur d'émettre un type anonyme qui met en œuvre ce générateur comme une machine d'état. C'est une approche formelle agréable qui est censé être vérifiable. C'est probablement les raisons pour lesquelles il regarde la façon dont il le fait.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top