这个编译器生成的枚举值是什么意思?

4

我写了一个相当复杂的方法,它 yield-returns 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个case中有一个空的switch,或者为什么m__Finallya会在finally块中被调用。(除了CER以外,在正常运行和在finally块中运行之间有语义上的区别吗?)

以下是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
3个回答

5
您没有展示原始迭代器块的样子,但我对Reflector和编译器生成的代码的经验是,它并不总是能够完全准确地反编译,因为编译器使用了一些IL,在C#中没有相应的等效项。
我有一篇关于迭代器块实现的文章,可能会对您有所帮助,但我不会过多担心编译后的代码看起来像什么。在某些情况下,C#编译器几乎肯定会生成不必要的代码,因为这可以使编译器更简单。迭代器块肯定很难做到正确(它可能变得非常复杂,包括finally块和迭代器处理),因此我认为只需信任JIT优化掉生成代码中不必要的部分,例如您生成的代码中的switch/case。

4

这只是反射器努力跟上已生成的IL(由于迭代器块不必关联“正常”的C#,只要它们是有效的IL)。特别是retfinally块之后。


1

我可以说C#编译器很蠢(它可能有点蠢)。当运行时对代码进行JIT编译时,这段代码看起来可能会非常不同(所有那些讨厌的垃圾都被省略了)。

无论如何,你可能熟悉状态机吧?当你在C#中编写生成器(yield stuff)时,你告诉编译器发出一个匿名类型,该类型将此生成器实现为状态机。这是一种很好的正式方法,旨在得到验证。这可能就是它看起来像这样的原因。


你可以相当肯定编译器是愚蠢的,因为所有程序都相当愚蠢。(这是哥德尔不完备定理的一个推论) - BCS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接