Если выражение, как представляется, оценивает, даже если условие оценивается как 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, исправленная в следующей версии.
Ответы
Ответ 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