Почему выбрасывание двух исключений подряд не приводит к предупреждению о недоступности кода?
Почему следующие строки кода не создают предупреждение компилятора?
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.