2010-01-25 4 views
44

Я столкнулся с ошибкой в ​​коде, которая воспроизводится только тогда, когда код построен с включенными оптимизациями. Я создал консольное приложение, которое реплицирует логику тестирования (код ниже). Вы увидите, что при включенной оптимизации «значение» становится нулевым после выполнения этой ошибочной логики:Ошибка только при оптимизации оптимизации компиляции

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

Исправления прямо вперед, и комментируется ниже кода обижая. Но ... Меня больше беспокоит, что я, возможно, столкнулся с ошибкой на ассемблере или, возможно, у кого-то есть объяснение, почему значение получает значение 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 является виртуальной машине под управлением Windows Server 2003 R2 с .NET 3.5 SP1 , Использование VS2008 Team System.

Спасибо,

Brian

+12

Ясно, что оптимизатор сердится на вас за использование '== false' вместо применения! оператора к выражению. – iandisme

+0

Ох, это * есть * интересно. Переход в отражатель ... –

+0

Haha bmancini

ответ

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, оптимизатор сделал вывод, что значение всегда будет нулевым, так что непосредственно проходит нуль в 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 

код был перемещен, но обратите внимание, что по адресу 5с теперь использует локальную переменную (значение) вместо ноль.

Вы можете сообщить об этой ошибке на веб-сайте connect.microsoft.com. Чтобы обойти эту проблему просто:

if (value != null) 
    { 
    Console.WriteLine("Post-check Value is: " + value); 
    new PrintManager().Print("blah", value); 
    } 
+2

Спасибо за великолепное объяснение того, что происходит. – bmancini

+6

это легко самый впечатляющий ответ, который я когда-либо видел в SO ... Или, может быть, меня просто впечатлило ... – Ben

+0

Не должно быть «if (! String.IsNullOrEmpty (value))», а вместо OP по сравнению с пустой строкой. –

1

Я на 64 и не может воспроизвести проблему на первый. Затем я указал цель как x86, и это случилось со мной. Вернемся к x64, и он ушел. Не уверен, что это значит, но я уже несколько раз возвращался назад и вперед.

+0

Я тоже на x64 и не могу воспроизвести это с помощью оптимизации = true и target = x86. – nos

2
value == new string[0] 

Вышеупомянутое выглядит как странное заявление. Вы сравниваете два строковых массива с выражением равенства. Это приведет только к истине, если оба указывают на один и тот же массив, что маловероятно. Это еще не объясняет, почему этот код ведет себя по-разному в оптимизированной версии.

+0

Не могу думать о каком-либо способе для инъекции перегрузки оператора для этого, но это определенно на 100% верно, что я удивляюсь? В то же время, это то, что я думал, так +1 –

0

Конечно выглядит как ошибка, она воспроизводит, когда вы поменять оператора операнды, как это?

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

Эта ошибка, по-видимому, исправлена ​​в .NET 4 (beta 2).Вот оптимизированный x86 разборка для бита nobugz подсвечивается, выше:

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

Программа также отображает ожидаемые результаты в обеих оптимизированных и неоптимизированных режимах.

Смежные вопросы