为什么multidimensional array的枚举值不等于自身?

考虑:

using System; public class Test { enum State : sbyte { OK = 0, BUG = -1 } static void Main(string[] args) { var s = new State[1, 1]; s[0, 0] = State.BUG; State a = s[0, 0]; Console.WriteLine(a == s[0, 0]); // False } } 

这怎么解释? 它在运行在x86 JIT中的Visual Studio 2015中的debugging版本中发生。 在x64 JIT中生成或运行的发行版按预期打印为True。

从命令行重现:

 csc Test.cs /platform:x86 /debug 

/debug:pdbonly/debug:portable/debug:full也可以重现。)

您在.NET 4 x86抖动中发现代码生成错误。 这是非常不寻常的一个,只有在代码不被优化时才会失败。 机器代码如下所示:

  State a = s[0, 0]; 013F04A9 push 0 ; index 2 = 0 013F04AB mov ecx,dword ptr [ebp-40h] ; s[] reference 013F04AE xor edx,edx ; index 1 = 0 013F04B0 call 013F0058 ; eax = s[0, 0] 013F04B5 mov dword ptr [ebp-4Ch],eax ; $temp1 = eax 013F04B8 movsx eax,byte ptr [ebp-4Ch] ; convert sbyte to int 013F04BC mov dword ptr [ebp-44h],eax ; a = s[0, 0] Console.WriteLine(a == s[0, 0]); // False 013F04BF mov eax,dword ptr [ebp-44h] ; a 013F04C2 mov dword ptr [ebp-50h],eax ; $temp2 = a 013F04C5 push 0 ; index 2 = 0 013F04C7 mov ecx,dword ptr [ebp-40h] ; s[] reference 013F04CA xor edx,edx ; index 1 = 0 013F04CC call 013F0058 ; eax = s[0, 0] 013F04D1 mov dword ptr [ebp-54h],eax ; $temp3 = eax ; <=== Bug here! 013F04D4 mov eax,dword ptr [ebp-50h] ; a == s[0, 0] 013F04D7 cmp eax,dword ptr [ebp-54h] 013F04DA sete cl 013F04DD movzx ecx,cl 013F04E0 call 731C28F4 

大量的临时代码和代码重复,这是未经优化的代码正常。 在013F04B8处的指令是显着的,即从sbyte到32位整数的必要转换发生的地方。 数组getter辅助函数返回0x0000000FF,等于State.BUG,并且需要将该值转换为-1(0xFFFFFFFF)才能进行比较。 MOVSX指令是Sign eXtension指令。

同样的事情发生在013F04CC,但这次没有 MOVSX指令进行相同的转换。 这是芯片掉落的地方,CMP指令比较0xFFFFFFFF和0x000000FF,这是错误的。 所以这是一个遗漏的错误,代码生成器未能再次发出MOVSX来执行相同的sbyte到int转换。

这个bug特别不寻常的是,当你启用优化器时,这个工作正常,现在知道在这两种情况下都使用MOVSX。

这个bug未被发现的可能原因是使用了sbyte作为枚举的基types。 相当罕见。 使用multidimensional array也是有用的,组合是致命的。

否则,我会说一个相当重要的错误。 很难猜到它有多广泛,我只能testing4.6.1的x86抖动。 x64和3.5 x86抖动产生非常不同的代码并避免这个错误。 继续进行的临时解决方法是将sbyte作为枚举基types删除,并将其设置为默认值int ,因此不需要任何符号扩展。

你可以在connect.microsoft.com上提交这个bug,链接到这个Q + A应该足以告诉他们他们需要知道的一切。 让我知道如果你不想花时间,我会照顾它。

让我们考虑OP的声明:

 enum State : sbyte { OK = 0, BUG = -1 } 

由于错误只发生在BUG为负数时(从-128到-1),而State是有符号字节的枚举,我开始假设在某个地方出现了投射问题。

如果你运行这个:

 Console.WriteLine((sbyte)s[0, 0]); Console.WriteLine((sbyte)State.BUG); Console.WriteLine(s[0, 0]); unchecked { Console.WriteLine((byte) State.BUG); } 

它会输出:

255

-1

BUG

255

由于我忽略的一个原因(截至目前) s[0, 0]在评估之前被转换为一个字节,这就是为什么它声称a == s[0,0]是假的。