Как обрабатывать несколько типов исключений в try-catch в C#?

«Как обрабатывать несколько типов исключений в try-catch в C#?» — вопрос из категории C# Core, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В 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("Операция чтения конфигурации завершена.");
}

Дополнительные возможности:

  1. Фильтры исключений (when): Позволяют добавить условие для входа в блок catch.

    catch (Exception ex) when (ex.InnerException is SqlException)
    {
        // Обрабатываем только если внутреннее исключение — SqlException
        LogDatabaseError(ex.InnerException);
    }
  2. Перехват нескольких типов в одном блоке (C# 6.0+):

    catch (ArgumentException or InvalidOperationException ex)
    {
        // Обрабатываем ArgumentException ИЛИ InvalidOperationException
        Console.WriteLine($"Некорректный аргумент или операция: {ex.Message}");
    }

Практические рекомендации:

  • Избегайте пустых блоков catch. Это скрывает ошибки.
  • Используйте повторный выброс (throw;), а не throw ex;. Первый вариант сохраняет оригинальный стек вызовов.
  • Блок finally идеально подходит для освобождения ресурсов (закрытие файлов, соединений с БД).