Если утверждение, кажется, оценивает, даже если условие оценивается как ложное - PullRequest
12 голосов
/ 19 мая 2011

Поздно вечером на работе мы пытались понять, почему что-то не получается.Проверка проверки завершилась неудачно, когда этого не должно было быть.

Мы закончили тем, что добавили оператор печати в этот код (дизассемблированный из Reflector для проверки того, что код на самом деле был тем, что мы написали):

public static string Redacted(string name, DateTime lastModified)
{
    long ticks = lastModified.Ticks;
    if ((ticks != (ticks - (ticks % 10000L))) &&
            (lastModified != DateTime.MaxValue))
    {
        Log.Debug(string.Format("Last Modified Date = '{0}'. Ticks = '{1}'. TicksCalc = '{2}'",
            lastModified.ToString("dd/MM/yyyy hh:mm:ss.fff"),
            ticks, ticks - (ticks % 10000L)));

Он напечатал (переформатировал):

Last Modified Date = '22/03/2011 12:16:22.000'.
Ticks     = '634363497820000000'.
TicksCalc = '634363497820000000'            

Но условие состоит в том, что "ticks" (что равно тикам, напечатанным выше) не равно "(ticks - (ticks % 10000))"(что равно TicksCalc)!634363497820000000! = 634363497820000000?!

Чтобы определить, что здесь происходит, мы добавили еще два утверждения:

long ticks = lastModified.Ticks;
/* Added following two lines: */
long num2 = ticks - (ticks % 10000L);
Log.Debug((ticks == num2).ToString());
/* */
if ((ticks != (ticks - (ticks % 10000L))) &&
        (lastModified != DateTime.MaxValue))
{
    Log.Debug(string.Format("Last Modified Date = '{0}'. Ticks = '{1}'. TicksCalc = '{2}'",
        lastModified.ToString("dd/MM/yyyy hh:mm:ss.fff"),
        ticks, ticks - (ticks % 10000L)));

Как и положено, этот выводит true (когдатестирование с тем же значением) и не записывал вторую строку.

Чувствуя себя немного потерянным, мы снова удалили две строки, перекомпилировали и перезапустили.Исходное поведение повторилось.

Этим утром я записал видео .

В видео в первую очередь показано попадание точки останова в метод с использованием «сломанного» кода, а затем повторная сборка и повторный запуск с использованием «рабочего» кода.Обратите внимание, что даже если отладчик показывает, что условие if оценивается как false, тело все еще вводится.

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

Более того, это происходит только в режиме Release (т. е. с включенной оптимизацией JIT).

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

Я надеюсь, что ответ не является чем-то очевидным, что я полностью упустил из виду ...!

Редактировать: Вот IL.Я не думаю, что с этим что-то не так, потому что он декомпилируется на правильный C #:

Обновление :

Подтверждено Microsoft как ошибка, исправленная в следующем выпуске .

Ответы [ 5 ]

9 голосов
/ 27 мая 2011

Я немного поэкспериментировал с упрощенным кодом: http://nopaste.info/2c99a0e028_nl.html

Самый интересный вариант:

static readonly long variableZero=0; 
const long constZero=0; 

public static void Broken2( long ticks2) 
 { 
     long ticks = ticks2+variableZero; 
     if (ticks != (ticks - (ticks % 10000L))) 
     { 
         string.Format("Last Modified Date = '{0}'. Ticks = '{1}'. TicksCalc = '{2}'", 
             "n/A", 
             ticks, ticks - (ticks % 10000L)).Dump(); 
     } 
 }

Если я заменим variableZero на constantZero, это будет работать.


Так что я уверен, что это ошибка джиттера или компилятора.

Я отправил отчет об ошибке в MS Connect: https://connect.microsoft.com/VisualStudio/feedback/details/671105/jitter-or-c-compiler-bug#details


Обновление : странное поведение возникает, только если отладчик не подключен.т.е. когда включена оптимизация Jit.Так что я почти уверен, что это ошибка джиттера.

А для людей без linq-pad теперь есть простой консольный проект на C #: http://nopaste.info/00a0e37328_nl.html

1 голос
/ 26 мая 2011

У меня было нечто подобное некоторое время назад.В моем случае это было связано с тем, что я сравнивал 2 целочисленных значения, где одно значение было ссылкой на целое в штучной упаковке, а другое было реальным примитивным значением.

Дело в том, что если вы печатаетеИз значения в штучной упаковке целого числа и примитива они выглядят одинаково, но сравнивать их - совсем другое.Вы получите сравнительное сравнение вместо сравнения значений.

Ответ прост:

long ticks = lastModified.Ticks;
long num2 = ticks - (ticks % 10000L);
if ((ticks != num2) && (lastModified != DateTime.MaxValue))
{ do your thing here! }
1 голос
/ 26 мая 2011

Это действительно похоже на - хм - джиттербаг.Можете ли вы установить точку останова в операторе «if» и показать нам снимок экрана с представлением «дизассемблирование» после его обращения?

1 голос
/ 19 мая 2011

Проверьте эту тему.

Если в Visual Studio 2008 странность операторов

Это сводится к тому, что вы не можете доверять отладчику все время.

Чтобы "исправить" оператор if, добавьте в него пустой оператор else {} .Отладчик будет работать как положено.

0 голосов
/ 20 мая 2011

Это безумие.Вы пытались, без веской причины, изменить порядок выражения if?

if (lastModified != DateTime.MaxValue && ticks != (ticks - (ticks % 10000L))

Кроме того, если это не сработает (как это не должно быть, учитывая, что это не должно быть проблемой в первом случае).место), можете ли вы показать фактический IL для кода в проблемной форме?

Еще одна вещь, нельзя ли упростить проверку ticks до:

(ticks % 10000L) != 0
...