«Обнаружен недоступный код» в MSIL или нативном коде - PullRequest
5 голосов
/ 27 декабря 2011

Компилирует ли компилятор «недостижимые коды» в MSIL или нативный код во время выполнения?

Ответы [ 3 ]

13 голосов
/ 27 декабря 2011

Вопрос немного неясен, но я попробую.

Прежде всего, ответ Адама верен, поскольку существует разница в IL, которую компилятор генерирует в зависимости от того, включен или выключен переключатель "оптимизировать". Компилятор гораздо агрессивнее удаляет недоступный код при включенном оптимизаторе.

Существует два вида недоступного кода, которые имеют отношение к делу. Сначала есть де-юре недоступный код; то есть код, который спецификация языка C # вызывает как недостижимый . Во-вторых, существует де-факто недоступный код; это код, который спецификация C # не вызывает как недоступный, но, тем не менее, не может быть достигнут. Из последнего типа недоступного кода есть код, который известен оптимизатору как недоступный, и есть код , неизвестный оптимизатору , чтобы быть недоступным.

Компилятор обычно всегда удаляет де-юре недоступный код, но удаляет де-факто недоступный код, если оптимизатор включен.

Вот пример каждого из них:

int x = 123;
int y = 0;
if (false) Console.WriteLine(1);
if (x * 0 != 0) Console.WriteLine(2);
if (x * y != 0) Console.WriteLine(3);

Все три строки Console.WriteLine недоступны. Первый - де-юре недоступен; компилятор C # утверждает, что этот код должен рассматриваться как недоступный для целей проверки определенного присваивания.

Вторые два де-юре достижимы, но де-факто недоступны. Они должны быть проверены на наличие определенных ошибок присваивания, но оптимизатору разрешено их удалять.

Из этих двух оптимизатор обнаруживает случай (2), но не случай (3). Оптимизатор знает, что целое число, умноженное на ноль, всегда равно нулю, и поэтому условие всегда ложно, поэтому он удаляет весь оператор.

В (3) случае оптимизатор не отслеживает возможные значения, назначенные y, и определяет, что y всегда равно нулю в точке умножения. Даже если вы и я знаем, что последствия недостижимы, оптимизатор этого не знает.

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

int z;
if (false) z = 123;
Console.WriteLine(z); // Error
if (false) Console.WriteLine(z); // Legal

Первое использование недопустимо, поскольку z не было определенно назначено при его использовании. Второе использование не незаконно, потому что код даже недоступен; z нельзя использовать до того, как он назначен, потому что управление никогда не попадет туда!

В C # 2 были некоторые ошибки, которые смешивали два вида достижимости. В C # 2 вы можете сделать это:

int x = 123;
int z;
if (x * 0 != 0) Console.WriteLine(z);

И компилятор не будет жаловаться, даже если de jure вызов Console.WriteLine достижим. Я исправил это в C # 3.0, и мы взяли последние изменения.

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

3 голосов
/ 27 декабря 2011

Компилятор C # может скомпилировать ваше приложение либо в конфигурации отладки, либо в конфигурации выпуска. Это изменяет поведение того, будет ли недоступный код скомпилирован в CIL и передан в выходной исполняемый файл. Давайте возьмем простую функцию, например:

public static int GetAnswer()
{
    return 42;
    Console.WriteLine("Never getting here!");
}

Когда вы компилируете это в конфигурации отладки, весь метод (включая недоступный код) передается как CIL. Это выглядело бы примерно так (возможно, с некоторыми добавленными nop инструкциями для облегчения отладки):

.method public static int32 GetAnswer() cil managed
{
    .maxstack 1
    .locals init (int32)

            ldc.i4.s 42 // load the constant 42 onto the stack
            stloc.0 // pop that 42 from the stack and store it in a local
            br.s L_000C // jump to the code at L_000C

            ldstr "Never getting here!" // load the string on the stack
            call void [mscorlib]System.Console::WriteLine(string) // call method

    L_000C: ldloc.0 // push that 42 onto the stack from the local
            ret // return, popping the 42 from the stack
}

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

Тем не менее, когда вы создаете свой проект в конфигурации выпуска, компилятор поймет, что, поскольку встроенная сборка не будет проходить в отладчике, она не будет генерировать недоступный код. CIL будет выглядеть так:

.method public static int32 GetAnswer() cil managed
{
   .maxstack 1

    ldc.i4.s 42 // load the constant 42 onto the stack
    ret // return, popping the 42 from the stack
}

Простой, чистый и оптимизированный.

0 голосов
/ 27 декабря 2011

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

if (false)
 // do something

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

Когда вы получаете сообщение об ошибке, что отладка не может быть продолжена из-за недоступного кода, это означает, что процесс, который вы отлаживаете, не соответствует исходному коду, который вы используете (разные версии и т. Д.) .

...