Почему выбрасывание двух исключений подряд не приводит к предупреждению о недоступности кода?

Почему следующие строки кода не создают предупреждение компилятора?

void Main()
{
  throw new Exception();
  throw new Exception();
}

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

3 ответа

Решение

Это явно ошибка компилятора, и она была введена в C# 3.0 - примерно в то время, когда я сильно реорганизовал проверку доступности. Это наверное мое плохо, извини.

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

Ошибка - не что иное, как отсутствующий регистр в генераторе предупреждений. У нас есть некоторый хитрый код, который гарантирует, что мы не сообщим о миллионе предупреждений, когда вы делаете недоступным какой-то большой раздел кода. У компилятора есть код для особого сообщения о предупреждениях о безусловных gotos ("goto", "break", "continue"), условных gotos ("if", "while" и т. Д.), Try-catch-finally (который включает эквивалент форм to try-catch-finally, например, блокировка и использование), блоки, возвраты (yield return и регулярный возврат), локальные объявления, помеченные операторы, переключатели и операторы выражений.

Вы видите "бросить заявления" в этом списке? И я нет. Это потому что мы забыли это.

Извиняюсь за неудобства. Я отправлю заметку в QA, и мы исправим это в будущей версии языка.

Спасибо, что обратили на это мое внимание.

Это может выдать предупреждение / ошибку компилятора, но, к сожалению, это не так. Но если вы посмотрите на код IL, рассматривается только первое исключение. Вы можете войти на connect.microsoft.com и поднять это как то, что вы хотели бы видеть.

если вы ILDasm код ниже

static void Main(string[] args)
        {
            Console.Write("Line 1");
           throw new Exception(); 
           throw new Exception();
           Console.Write("Line 4");
        }

Вы получите это

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       18 (0x12)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "Line 1"
  IL_0006:  call       void [mscorlib]System.Console::Write(string)
  IL_000b:  nop
  IL_000c:  newobj     instance void [mscorlib]System.Exception::.ctor()
  IL_0011:  throw
} // end of method Program::Main

После первого объекта Exception ничто другое не преобразуется в IL.

Эта ошибка (как Липперт называет это выше) имеет некоторые странные последствия. Конечно, такой код также не дает предупреждений во время компиляции:

static int Main()
{
  return 0;

  throw new Exception("Can you reach me?");
}

Если вы креативны, вы все равно можете сделать throw Заявление вызывает (не связанные) предупреждения. В этом любопытном примере код генерирует предупреждение только потому, что "зеленый" недоступен:

static int Main()
{
  return 0;

  throw new Exception(((Func<string>)(() => { if (2 == 2) { return "yellow"; } return "green"; }))());
}

(код просто создает экземпляр делегата из лямбды и вызывает делегат).

Но этот пример проще и кажется хуже:

static int Main()
{
  int neverAssigned;

  return 0;

  throw new Exception(neverAssigned.ToString());
}

Этот последний пример кода также компилируется без предупреждения! Там нет проблем в использовании neverAssigned потому что "использование" недоступно. Но вы также не получаете предупреждения о локальной переменной, которая никогда не назначается (и никогда не "действительно" читается). Так что, повторюсь, вообще никакого предупреждения, что кажется очень неправильным.

Интересно, будет ли это поведение изменено в будущих версиях Visual C#? Его изменение даст людям предупреждения, которых у них не было раньше (что, на мой взгляд, они заслуживают).

Дополнение: Это поведение, по-видимому, остается неизменным при использовании компилятора Visual Studio 2015 на основе Roslyn C# 6.0.

Другие вопросы по тегам