Магия компилятора: почему?

Я только заметил, что с учетом следующего кода:

if (x.ID > 0 && !x.IsCool)

компилятор Microsoft С# 3.0 (VS2008 SP1) оптимизирует его для этого:

if (!((x.Id <= 0) || x. IsCool))

Это в сборке Debug без включения оптимизации. Почему компилятор делает это? Это быстрее с точки зрения исполнения?

Я использовал Reflector, чтобы найти это (я действительно искал что-то другое)

Ответ 1

Компилятор С#, безусловно, не генерирует эквивалентный код С# для вашего фрагмента. Он составлен до ИЛ. В принципе, то, что вы видите (из Reflector, я думаю), является эквивалентным кодом С#, который выделяет decompiler для этого IL.

  • Спецификация языка не говорит, что такое "неоптимизированный" код. Компилятору С# разрешено создавать любой действительный, функционально эквивалентный код. Даже без включения оптимизации компилятор может выполнить базовую оптимизацию. Кроме того, вы не можете сказать, что является естественным для компилятора, и компилятор сознательно оптимизировал его или нет.

  • Оператор if в целом оценивается как последовательность условных ветвей, основанная на значениях каждого отдельного выражения, указанного в предложении "и". Выражение не оценивается в одном блоке кода с инструкциями "и". Вывод декомпилятора - это то, что выведено из этих ветвей. Декомпилятор не всегда может вывести исходное выражение, которое вы написали. Он просто выводит что-то эквивалентное.

Аналогично, разница между этим фрагментом:

if (a) { something(); }
else { somethingElse(); }

и этот фрагмент:

if (!a) { somethingElse(); }
else { something(); }

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

Ответ 2

Я думаю, что эти два выражения в точности эквивалентны с точки зрения семантики языка. Оба способа включают короткое замыкание.

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

ИЗМЕНИТЬ

Итак, просто подытожим:

Вопрос OP задает вопрос: "Почему эта оптимизация происходит".

На самом деле "оптимизация" не происходит. Два исходных кода С# логически эквивалентны. ".Net Reflector" или какой-либо другой инструмент разборки, возможно, может декомпилировать тот же IL в тот или иной. На уровне IL есть только куча условных переходов, и поэтому нет необходимости в том, чтобы знать, "какой путь есть, если и что еще", или другие подобные эквиваленты DeMorgan.

Удивительно, люди очень счастливы проголосовать вверх или вниз ответы на этот вопрос, даже если (или, возможно, потому что) исходный вопрос не имеет большого смысла (или полагается на ошибочное предположение).

К счастью, в конечном итоге преобладает мудрость толпы (и умных людей, таких как @Mehrdad). Ура для StackOverflow!

(Я делаю свой ответ вики, потому что я не хочу повторять "рассказ о вопросе", когда rep должен быть награжден "хорошим ответом на вопрос". Но я думаю, что история этого вопроса интересна.)

Ответ 3

Компиляция с помощью кода операции условного перехода. Извлечение отрицания из выражения позволяет оптимизировать его путем замены конечной цели условного перехода на падающий блок.