44

我在代码中遇到了一个错误,只有在启用优化的情况下构建代码时才会重现该错误。我制作了一个控制台应用程序,它复制了测试逻辑(下面的代码)。您会看到,启用优化后,执行此无效逻辑后,“值”变为空:

if ((value == null || value == new string[0]) == false)

修复很简单,并在有问题的代码下方注释掉。但是......我更担心我可能在汇编程序中遇到了一个错误,或者其他人可能解释了为什么 value 设置为 null。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace memory_testing
{
    class Program
    {
        sta tic void Main(string[] args)
        {
            while(true)
            {
                Console.Write("Press any key to start...");
                Console.ReadKey();
                Console.WriteLine();
                PrintManagerUser c = new PrintManagerUser();
                c.MyProperty = new string[1];
            }
        }
    }

    public class PrintManager
    {
        public void Print(string key, object value)
        {
            Console.WriteLine("Key is: " + key);
            Console.WriteLine("Value is: " + value);
        }
    }

    public class PrintManagerUser
    {
        public string[] MyProperty
        {
            get { return new string[100]; }
            set
            {
                Console.WriteLine("Pre-check Value is: " + value);
                if ((value == null || value == new string[0]) == false)
                {
                    Console.WriteLine("Post-check Value is: " + value);
                    new PrintManager().Print("blah", value);
                }
                //if (value != null && value.Length > 0)
                //{
                //    new PrintManager().Print("blah", value);
                //}
            }
        }
    }
}

正常输出应该是:

Pre-check Value is: System.String[]
Post-check Value is: System.String[]
Key is: blah
Value is: System.String[]

错误的输出是:

Pre-check Value is: System.String[]
Post-check Value is:
Key is: blah
Value is:   

我的 Env 是一个运行带有 .NET 3.5 SP1 的 Windows Server 2003 R2 的 VM。使用VS2008团队系统。

谢谢,

布赖恩

4

5 回答 5

45

是的,您的表达使 JIT 优化器严重混淆。生成的机器代码如下所示:

                if ((value == null || value == new string[0]) == false)
00000027  test        esi,esi               ; value == null?
00000029  je          00000075 
0000002b  xor         edx,edx               ; new string[0]
0000002d  mov         ecx,6D913BD2h 
00000032  call        FFD20BC8 
00000037  cmp         eax,esi               ; (value == new string[0]) == false?
00000039  je          00000075 
                {
                    Console.WriteLine("Post-check Value is: " + value);
0000003b  mov         ecx,dword ptr ds:[03532090h]  ; "Post-check value is: "
00000041  xor         edx,edx               ; BUGBUG not null!
00000043  call        6D70B7E8              ; String.Concat()
00000048  mov         esi,eax               ; 
0000004a  call        6D72BE08              ; get Console.Out
0000004f  mov         ecx,eax 
00000051  mov         edx,esi 
00000053  mov         eax,dword ptr [ecx] 
00000055  call        dword ptr [eax+000000D8h]     ; Console.WriteLine()

该错误发生在地址 41,优化器得出的结论是 value 将始终为 null,因此它直接将 null 传递给 String.Concat()。

为了比较,这是关闭 JIT 优化时生成的代码:

                    Console.WriteLine("Post-check Value is: " + value);
00000056  mov         ecx,dword ptr ds:[03342090h] 
0000005c  mov         edx,dword ptr [ebp-8] 
0000005f  call        6D77B790 

代码已移动,但请注意,在地址 5c 处,它现在使用局部变量(值)而不是 null。

您可以在 connect.microsoft.com 上报告此错误。解决方法很简单:

  if (value != null)
  {
    Console.WriteLine("Post-check Value is: " + value);
    new PrintManager().Print("blah", value);
  }
于 2010-01-25T21:46:19.010 回答
3

此错误似乎已在 .NET 4(beta 2)中修复。这是上面突出显示的位 nobugz 的优化 x86 反汇编:

                    Console.WriteLine("Post-check Value is: " + value);
00000056  mov         ecx,dword ptr ds:[033C2090h] 
0000005c  mov         edx,dword ptr [ebp-8] 
0000005f  call        65D8FE10

该程序还以优化和未优化模式显示预期输出。

于 2010-01-28T14:50:18.917 回答
2
value == new string[0]

以上对我来说似乎是一个奇怪的陈述。您正在使用 equals 语句比较两个字符串数组。如果它们都指向同一个数组,那只会导致 true,这是不太可能的。这还没有解释为什么这段代码在优化版本中表现不同。

于 2010-01-25T21:42:49.297 回答
1

我在 x64 上,一开始无法重现该问题。然后我将目标指定为x86,它发生在我身上。回到 x64,它就消失了。不知道这到底意味着什么,但我现在已经来回走了几次。

于 2010-01-25T21:20:23.877 回答
0

当然看起来像一个错误,当你像这样交换运算符操作数时它会重现吗?

if (false == (null == value || new string[0] == value))
于 2010-01-25T21:47:23.577 回答