2011-12-27 2 views
4

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

+0

Ваш вопрос нуждается в большем контексте, поскольку не все MSIL генерируется компилятором. Когда вы получите сообщение об ошибке? Что собирается делать сборка, которую вы загружаете? Это ваше или третье лицо? – dasblinkenlight

+0

Это просто из-за моего любопытства. Пример кода: 'if (1 == 1) {// Reachable} else {// обнаружен недостижимый код}'. Скомпилирует недостижимую часть? Он выделяет память? – Babak

+0

Вы конкретно задаете вопрос о недостижимом коде, который компилятор знает и предупреждает, недоступен или вы также спрашиваете о более сложных случаях, таких как 'if (i == 1)', где вы, программист, знаете, что 'i' всегда будет быть четным числом? – hvd

ответ

12

Вопрос немного неясен, но я сделаю снимок.

Во-первых, ответ Адама является правильным, поскольку существует различие в ИЛ, которое компилятор испускает в зависимости от того, включен или выключен переключатель «оптимизация». Компилятор гораздо более агрессивен в отношении удаления недостижимого кода с включенным оптимизатором.

Существует два вида недостижимого кода. Первый - de jure недоступный код; то есть код, который спецификация языка C# вызывается как недоступна. Во-вторых, есть de facto недостижимый код; это код, который спецификация 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.WriteLines недостижимы. Первый - de jure недоступен; компилятор 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); 

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

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

+2

И вот почему вы удивительны. +1 и спасибо! –

0

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

if (false) 
// do something 

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

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

+0

Вы проверили? Потому что у меня есть, и то, что я вижу, не соответствует тому, что вы описываете. Если я попытаюсь заставить отладчика вступить в недостижимый блок кода, просто этого не произойдет. Он выбирает конец блока в качестве следующей инструкции. – hvd

+0

Некоторый код не компилируется независимо. Убедитесь, что ваш недостижимый код выполняет что-то другое, кроме выделения памяти. Отладчик всегда переходит к описанию переменной.Поэтому в выражении if объявите переменную, а затем используйте ее. Но чтобы ответить на ваш вопрос, я специально не проверил этот пример. –

+0

Мой пример состоит из 'static void Main() {if (1 == 0) {Console.WriteLine (« Вселенная взорвалась »); }} ', и сгенерированный ИЛ не содержит вызова' Console.WriteLine'. – hvd

3

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

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

При компиляции этого в конфигурации отладки, весь метод (в том числе недостижимого кода) испускается в КСС. Это будет выглядеть примерно так (возможно, с некоторыми дополнительными 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

Снова (то же, что и с другим ответом): вы проверили? Поскольку компилятор в моей системе не содержит кода, который, как он знает, недоступен в сгенерированном исполняемом файле, даже не в режиме отладки. – hvd

+1

Итак, я просто проверил это с помощью Visual Studio 2010 SP1 и .NET Framework 4; вот странная часть: в режиме отладки компилятор ** сделал ** выделяет дополнительное хранилище локального (в отличие от использования стека для литерала 42) и безусловной ветви, но он * не * излучал вызовите 'Console :: WriteLine (string)'. Я сбит с толку ... Я знаю, что видел это поведение раньше, возможно, в предыдущих версиях компилятора или .NET Framework (поэтому я мог написать это CIL вручную), так что, похоже, что-то могло измениться , или есть силы в игре, о которых я не знаю. –

+0

Где Эрик Липперт, когда он нам нужен? –