Подтвердить что ты не робот

Если выражение, как представляется, оценивает, даже если условие оценивается как false

Late At Work вчера вечером мы пытались понять, почему что-то не удается. Проверка проверки не срабатывала, когда она не была.

Мы закончили тем, что добавили инструкцию для печати к этому коду (разобрали из 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 - (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).

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

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

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

Обновление

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

4b9b3361

Ответ 1

Я немного экспериментировал с упрощенным кодом: 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, он будет работать.


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

Я зарегистрировал bugreport на MS Connect: https://connect.microsoft.com/VisualStudio/feedback/details/671105/jitter-or-c-compiler-bug#details


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

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

Ответ 2

Посмотрите эту тему.

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

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

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

Ответ 3

Это действительно выглядит как ahem-jitterbug. Можете ли вы установить точку останова в инструкции "if" и показать скриншот просмотра разметки после его появления?

Ответ 4

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

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

Ответ прост:

long ticks = lastModified.Ticks;
long num2 = ticks - (ticks % 10000L);
if ((ticks != num2) && (lastModified != DateTime.MaxValue))
{ do your thing here! }

Ответ 5

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

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

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

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

(ticks % 10000L) != 0