Как перехватить несколько исключений в C#: полный гайд

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Поймать разом: как перехватить несколько исключений в C# без дублирования кода

Представьте: вы пишете приложение, которое работает с файлами, сетью и базами данных. В одном методе может вылететь FileNotFoundException, IOException или UnauthorizedAccessException. Классический подход — три блока catch с одинаковым кодом внутри. Это нарушает принцип DRY (Don't Repeat Yourself) и превращает код в "спагетти".

Хорошая новость: в C# есть несколько элегантных способов перехватить несколько исключений сразу. В этом туториале мы разберём все варианты — от базовых до продвинутых — и вы узнаете, как писать чистый, поддерживаемый код обработки ошибок.

Почему это важно? Реальный сценарий

Допустим, вы разрабатываете консольное приложение для резервного копирования. Оно читает файлы, сжимает их и отправляет на удалённый сервер. На каждом этапе могут возникнуть разные исключения:

  • Чтение файла: FileNotFoundException, PathTooLongException
  • Сжатие: OutOfMemoryException
  • Отправка: HttpRequestException, TimeoutException

Если вы будете писать отдельный catch для каждого исключения с одинаковым логированием, код станет громоздким. А если позже потребуется изменить логику — придётся править в трёх местах. Именно здесь и пригодится умение ловить несколько исключений за раз.

Способ 1. Классический: несколько блоков catch (до C# 6)

Самый простой и очевидный метод — написать несколько блоков catch подряд. C# сам выберет нужный блок по типу исключения:

try
{
    // Код, который может выбросить разные исключения
    string content = File.ReadAllText(@"C:\data\config.txt");
    int value = int.Parse(content);
    await SendToServerAsync(value);
}
catch (FileNotFoundException ex)
{
    Console.WriteLine($"Файл не найден: {ex.Message}");
    LogError(ex);
}
catch (FormatException ex)
{
    Console.WriteLine($"Неверный формат данных: {ex.Message}");
    LogError(ex);
}
catch (HttpRequestException ex)
{
    Console.WriteLine($"Ошибка сети: {ex.Message}");
    LogError(ex);
}

Плюсы: прозрачно, легко читается, можно обрабатывать каждое исключение по-своему.
Минусы: если действия одинаковые — дублирование кода.

Способ 2. Перехват нескольких исключений в одном catch (C# 6+)

Начиная с C# 6, вы можете перечислить несколько типов исключений через | в одном блоке catch. Это уменьшает дублирование и сохраняет читаемость:

try
{
    // Рискованный код
}
catch (FileNotFoundException | FormatException | HttpRequestException ex)
{
    Console.WriteLine($"Произошла ошибка: {ex.Message}");
    LogError(ex);
    // Дополнительная логика, общая для всех трёх типов
}

Важно: все исключения должны быть совместимы по иерархии. Нельзя смешивать, например, IOException и NullReferenceException — они не связаны общим предком, кроме Exception. Но в C# 6+ это разрешено, компилятор просто приведёт переменную ex к общему базовому типу.

Когда использовать этот способ?

  • Когда логика обработки идентична для всех исключений.
  • Когда вы хотите избежать вложенных блоков if или switch.
  • В небольших методах, где важна краткость.

Способ 3. Фильтры исключений (C# 6+)

Фильтры исключений — мощный механизм, который позволяет ловить исключения по условию с помощью ключевого слова when. Вы можете перехватить одно и то же исключение, но с разной логикой в зависимости от его свойств:

try
{
    // Код
}
catch (IOException ex) when (ex is FileNotFoundException || ex is DirectoryNotFoundException)
{
    Console.WriteLine("Проблема с файлом или папкой");
}
catch (IOException ex) when (ex is UnauthorizedAccessException)
{
    Console.WriteLine("Нет прав доступа");
}
catch (Exception ex) when (DateTime.Now.DayOfWeek == DayOfWeek.Monday)
{
    Console.WriteLine("По понедельникам ловим все ошибки!");
}

Ключевой момент: фильтры выполняются до входа в блок catch. Если условие when возвращает false, исключение не перехватывается, и стек поиска продолжается. Это позволяет создавать очень гибкие конструкции.

Реальный пример с фильтрами

Представьте, что вы обрабатываете загрузку файлов. Ошибка "файл занят" (статус блокировки) должна прервать операцию, а "файл не найден" — просто записаться в лог:

try
{
    UploadFile("report.xlsx");
}
catch (IOException ex) when (ex.Message.Contains("занят другим процессом"))
{
    Console.WriteLine("Файл заблокирован. Операция отменена.");
    throw; // Пробрасываем дальше
}
catch (IOException ex) when (ex.Message.Contains("не найден"))
{
    LogWarning(ex);
    // Продолжаем выполнение
}

Фильтры исключений — это элегантный способ избежать вложенных if внутри блока catch.

Способ 4. Общий предок: базовый класс Exception

Иногда самый простой способ — перехватить все исключения через базовый класс Exception и уже внутри проверить тип:

try
{
    // Код
}
catch (Exception ex) when (ex is IOException or HttpRequestException or FormatException)
{
    Console.WriteLine($"Ошибка: {ex.Message}");
    // Общая обработка
}

Или без фильтра, но с проверкой внутри:

try
{
    // Код
}
catch (Exception ex)
{
    if (ex is IOException || ex is HttpRequestException || ex is FormatException)
    {
        Console.WriteLine($"Ошибка: {ex.Message}");
    }
    else
    {
        throw; // Пробрасываем неожиданные исключения
    }
}

Предупреждение: перехватывать Exception без фильтра — плохая практика. Вы рискуете проглотить критические ошибки вроде OutOfMemoryException или StackOverflowException. Всегда используйте when или пробрасывайте необработанные исключения.

Способ 5. AggregateException и Task.WhenAll

Если вы работаете с асинхронными операциями, особенно с Task.WhenAll, исключения могут быть агрегированы в AggregateException. Перехватить все внутренние исключения можно так:

try
{
    Task task1 = DownloadFileAsync("file1.zip");
    Task task2 = DownloadFileAsync("file2.zip");
    await Task.WhenAll(task1,                

Блоги

Рекомендаций по книгам