Ответ
Блоки catch должны быть указаны в порядке от наиболее специфичного исключения к наиболее общему. Компилятор C# требует этого и выдаст ошибку CS0160, если более общий блок перехватит исключение раньше производного от него.
Правильный порядок:
try
{
// Код, который может вызвать исключение
File.ReadAllText("missing.txt");
}
catch (FileNotFoundException ex) // Наиболее специфичное
{
// Обрабатываем именно случай отсутствия файла
Console.WriteLine($"Файл не найден: {ex.FileName}");
// Можно создать файл по умолчанию
}
catch (IOException ex) // Более общее (FileNotFoundException наследуется от него)
{
// Обрабатываем другие ошибки ввода-вывода (нет прав, диск полон и т.д.)
Console.WriteLine($"Ошибка IO: {ex.Message}");
_logger.LogWarning(ex, "IO error occurred");
}
catch (Exception ex) // Самое общее исключение
{
// Перехватываем всё остальное (OutOfMemoryException, ArgumentException и пр.)
// Обычно здесь только логирование и аварийное завершение операции
_logger.LogError(ex, "Unexpected error");
throw; // Пробрасываем дальше, если не можем восстановиться
}
Почему порядок важен: Выполняется первый блок catch, тип исключения которого совместим с выброшенным исключением. Если catch (Exception) будет первым, все последующие блоки станут недостижимым кодом.
Ответ 18+ 🔞
А, ну ты посмотри, какой умный нашелся! Решил все исключения в одну кучу свалить, да? Так, слушай сюда, дружок-пирожок, сейчас объясню, как не облажаться с этими catch блоками.
Представь, что исключения — это как бы твои родственники на семейном сборище. FileNotFoundException — это мелкий пиздюк, который потерял ключи. IOException — его уже более серьёзный отец, у которого проблемы посерьёзнее (диск сдох, прав нет). А Exception — это вообще ихний древний прадед, который всех породил и может появиться по любому поводу, от нехватки памяти до того, что ты тупо аргумент не тот передал.
Так вот, если ты этого деда-Exception поставишь встречать гостей первым, он всех загородит собой! Мелкий пиздюк прибежит — дед его уже обнял и в дом затащил. Отец приедет — та же история. Остальные ловильщики (catch) просто стоят и пальцем хуярят, потому что до них очередь никогда не дойдёт. Компилятор C# на это смотрит и такой: «Ну ты даёшь, мудила!» — и выдает ошибку CS0160. Недостижимый код, блядь!
Поэтому расставляй их по старшинству, от младшего к старшему. Сначала лови конкретную хрень, потом более общую, а уж самого всеядного деда — в самый конец. Он пусть ловит то, с чем остальные не справились.
try
{
// Пытаемся что-то сделать
File.ReadAllText("missing.txt");
}
catch (FileNotFoundException ex) // Ловим сначала мелкого (самое специфичное)
{
// О, файлик потерялся! Чётко знаем, что делать.
Console.WriteLine($"Бля, файл-то где? {ex.FileName}");
// Можно, короче, новый создать
}
catch (IOException ex) // Потом ловим его папаню (более общее)
{
// А тут уже другие проблемы с вводом-выводом: диск залочен, сеть отвалилась...
Console.WriteLine($"Чёт IO глючит: {ex.Message}");
_logger.LogWarning(ex, "IO error occurred");
}
catch (Exception ex) // И только в самом конце — деда на все случаи жизни
{
// Ловим всё остальное, что летит из try-блока
// Сюда обычно попадает какая-то неожиданная дичь
_logger.LogError(ex, "Ёбаный насос! Ничего не понял, но очень интересно!");
throw; // И обычно — кидаем эту проблему дальше, на уровень выше
}
Запомни раз и навсегда: от конкретного к общему. И тогда у тебя код будет не только компилироваться, но и выглядеть, как у адекватного человека, а не как у того, кто на catch (Exception) молится.