Ответ
В C# для обработки нескольких типов исключений используются последовательные блоки catch. Ключевой принцип — располагать их в порядке от наиболее специфичных к наиболее общим, чтобы гарантировать корректный перехват.
Пример с несколькими блоками catch:
try
{
// Код, который может вызвать разные исключения
var data = File.ReadAllText("config.json");
var settings = JsonSerializer.Deserialize<Settings>(data);
}
catch (FileNotFoundException ex) // Наиболее специфичное
{
Console.WriteLine($"Файл конфигурации не найден: {ex.FileName}");
// Создаем файл с настройками по умолчанию
}
catch (JsonException ex)
{
Console.WriteLine($"Ошибка парсинга JSON: {ex.Message}");
// Используем дефолтные настройки
}
catch (IOException ex) // Более общее исключение ввода-вывода
{
Console.WriteLine($"Ошибка ввода-вывода: {ex.Message}");
}
catch (Exception ex) // Самый общий блок — должен быть последним
{
Console.WriteLine($"Непредвиденная ошибка: {ex.Message}");
throw; // Пробрасываем выше, если не можем обработать
}
finally
{
// Этот блок выполнится всегда, даже если было исключение
Console.WriteLine("Операция чтения конфигурации завершена.");
}
Дополнительные возможности:
-
Фильтры исключений (when): Позволяют добавить условие для входа в блок
catch.catch (Exception ex) when (ex.InnerException is SqlException) { // Обрабатываем только если внутреннее исключение — SqlException LogDatabaseError(ex.InnerException); } -
Перехват нескольких типов в одном блоке (C# 6.0+):
catch (ArgumentException or InvalidOperationException ex) { // Обрабатываем ArgumentException ИЛИ InvalidOperationException Console.WriteLine($"Некорректный аргумент или операция: {ex.Message}"); }
Практические рекомендации:
- Избегайте пустых блоков
catch. Это скрывает ошибки. - Используйте повторный выброс (
throw;), а неthrow ex;. Первый вариант сохраняет оригинальный стек вызовов. - Блок
finallyидеально подходит для освобождения ресурсов (закрытие файлов, соединений с БД).
Ответ 18+ 🔞
Смотри, про исключения в C# — это как с женой в магазине. Если она сначала начнёт орать про то, что ты вообще мудак и всё забываешь, а потом уже про конкретную соль, которую не купил, — ты нихуя не поймёшь. Так и тут: ловить надо от конкретного к общему, а то общий Exception всё схавает, как я водку в пятницу, и более специфичные обработчики просто не сработают.
Вот смотри, как это выглядит в коде, на реальном примере:
try
{
// Код, который может вызвать разные исключения
var data = File.ReadAllText("config.json");
var settings = JsonSerializer.Deserialize<Settings>(data);
}
catch (FileNotFoundException ex) // Самый конкретный случай — файла нет
{
Console.WriteLine($"Файл конфигурации не найден: {ex.FileName}");
// Создаем файл с настройками по умолчанию
}
catch (JsonException ex) // Чуть более общее — файл есть, но внутри какая-то хуйня
{
Console.WriteLine($"Ошибка парсинга JSON: {ex.Message}");
// Используем дефолтные настройки
}
catch (IOException ex) // Ещё общее — любая ошибка ввода-вывода
{
Console.WriteLine($"Ошибка ввода-вывода: {ex.Message}");
}
catch (Exception ex) // Абсолютно всё, что летит — ловим в самом конце!
{
Console.WriteLine($"Непредвиденная ошибка: {ex.Message}");
throw; // И да, кидаем это добро дальше, если сами не справились
}
finally
{
// Этот блок выполнится ВСЕГДА, даже если всё пошло по пизде.
// Как твоя тёща, которая заявится в гости, даже если у тебя похмелье овердохуища.
Console.WriteLine("Операция чтения конфигурации завершена.");
}
А ещё есть фишки, которые жизнь облегчают:
-
Фильтры (when): Это как сказать "я налью тебе только если ты не трогал моё пиво".
catch (Exception ex) when (ex.InnerException is SqlException) { // Сработает только если внутри сидит исключение от базы данных LogDatabaseError(ex.InnerException); } -
Несколько типов в одном catch (C# 6.0+): Чтоб не плодить одинаковый код.
catch (ArgumentException or InvalidOperationException ex) { // Ловим либо одно, либо другое — похуй, обрабатываем одинаково Console.WriteLine($"Некорректный аргумент или операция: {ex.Message}"); }
Главное, что нужно запомнить:
- Не оставляй пустые
catchблоки. Это пиздец как плохо. Сказал бы тебе почему, но ты всё равно забудешь, а ошибка потом всплывёт в самом неожиданном месте, и будешь ебаться с дебагом полдня. - Если кидаешь исключение заново — используй голый
throw;, а неthrow ex;. Первый вариант не трогает стек вызовов, и ты увидишь, откуда ошибка пришла на самом деле. Второй — затрёт историю, и ты будешь гадать, какого хуя. - Блок
finally— твой лучший друг для уборки. Закрывай там файлы, соединения и прочие ресурсы. Он выполнится в любом случае, даже если вtryвсё взорвалось.