JavaRush /Курсы /C# SELF /Множественный catch и фильтры в C#

Множественный catch и фильтры в C#

C# SELF
13 уровень , 4 лекция
Открыта

1. Введение

Когда программа становится чуть сложнее, чем Hello, world!, вы быстро сталкиваетесь с задачей: обрабатывать ошибки разных типов по-разному. Представьте: вы работаете с файлами, сетью, БД — и для разных ошибочных сценариев реакция должна быть разной. Например, при отсутствии файла можно предложить пользователю выбрать другой, а при ошибке сети — повторить операцию или показать ободряющее сообщение "Проверьте кабель, вдруг кошка его опять перегрызла".

В C# для этого используют несколько блоков catch подряд. Каждый такой блок специализируется на своём типе исключения (и его наследниках).

Структура множественного catch


try
{
    // Здесь — код, который может выбросить разные исключения
}
catch (FileNotFoundException ex)
{
    Console.WriteLine($"Файл не найден: {ex.Message}");
}
catch (IOException ex) // ловит все ошибки ввода/вывода, если это не FileNotFoundException
{
    Console.WriteLine($"Ошибка ввода-вывода: {ex.Message}");
}
catch (Exception ex)
{
    Console.WriteLine($"Что-то пошло не так: {ex.Message}");
}
Множественные блоки catch для разных типов ошибок

Важно! Компилятор идёт по блокам сверху вниз и выбирает первый, который подходит по типу. Если поймался FileNotFoundException, дальше по цепочке он уже не пойдёт.

Иллюстрация "цепочки"

Тип исключения Поймает какой блок?
FileNotFoundException Первый catch
IOException (другой) Второй catch
ArgumentException Третий (общий) catch

Почему нельзя смешивать?
Самую "широкую" ловушку — например, с catch (Exception) — всегда ставьте последней, иначе она "поглотит" все исключения раньше времени, и до специализированных catch-ов исполнение не дойдёт.

Это как выключать все пожарные датчики в здании, потому что один из них сработал на подгоревший тостер: в таком случае последующие пожары система не заметит.

Рабочий пример

using System;
using System.IO;

class Program
{
    static void Main()
    {
        try
        {
            double result = CalculateAverageAgeFromFile("users.txt");
            Console.WriteLine($"Средний возраст — {result}");
        }
        catch (FileNotFoundException ex)
        {
            Console.WriteLine("Ошибка: файл не найден. Проверьте путь.");
        }
        catch (FormatException ex)
        {
            Console.WriteLine("Ошибка данных: не удаётся прочитать возраст.");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Другая ошибка: {ex.Message}");
        }
    }

    static double CalculateAverageAgeFromFile(string filePath)
    {
        // (Реализация: читает файл, парсит возраста, вычисляет среднее)
        // ...
        throw new NotImplementedException();
    }
}

Здесь мы явно разделяем, что делать, если файла нет (FileNotFoundException), а что — если в файле какие-то "кривые" данные (FormatException). Все остальные случаи возьмёт "запасной" третий блок.

2. Фильтры catch: ловим тонкие нюансы

Множественные блоки catch удобны, но иногда этого мало. Бывает, что внутри одного типа исключения хочется реагировать по-разному, в зависимости от обстоятельств.

Например, если сеть не работает потому что закончился интернет — мы можем попробовать переподключить. А если сервер просто не отвечает — возможно, стоит показать другое сообщение.

Вот тут и вступают в бой фильтры catch — настоящая суперфича C#, которая позволяет отлавливать не только по типу, но и по дополнительному условию.

Синтаксис фильтра when


catch (IOException ex) when (ex.Message.Contains("диска нет"))
{
    Console.WriteLine("Упс! Похоже, вы выдернули флешку.");
}
catch (IOException ex)
{
    Console.WriteLine("Другая ошибка ввода/вывода: " + ex.Message);
}
Фильтр catch when для уточнения условий перехвата

Тут первый блок ловит только те IOException, чьё сообщение содержит фразу "диска нет" — всё остальное уходит во второй блок.

Применение в реальной жизни

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

Ещё пример: представьте, у нас в методе, который парсит файл, мы хотим не просто "ругающийся" FormatException, а отдельно — если речь идёт о возврате возраста (например, если возраст записан как "abc" вместо числа).

catch (FormatException ex) when (ex.Message.Contains("возраст"))
{
    Console.WriteLine("Ошибка: не удалось прочитать возраст. Проверьте данные.");
}
catch (FormatException ex)
{
    Console.WriteLine("Ошибка формата данных: " + ex.Message);
}

Фильтры и производительность

Фильтры — штука удобная, но помните, что условие в фильтре вычисляется до входа в блок catch, то есть если оно не выполняется — тело блока вовсе не трогается.

Кстати, если фильтр выбрасывает исключение сам по себе (например, в вашем выражении в when случается деление на нуль) — то такое исключение этот блок catch никогда не поймает. С ним нужно быть аккуратнее.

3. Совмещаем множество catch и фильтры

Представьте, что в вашем проекте надо обработать не только тип исключения, но и его внутреннее состояние. Например, при работе с IOException мы хотим разное поведение, если ошибка в файловой системе связана с доступом или с отсутствием места на диске.


try
{
    File.AppendAllText("log.txt", "Новая запись\n");
}
catch (IOException ex) when (ex.Message.Contains("Нет места"))
{
    Console.WriteLine("Ошибка: На диске закончилось место!");
}
catch (UnauthorizedAccessException)
{
    Console.WriteLine("Ошибка: нет прав для записи файла. Запустите от имени администратора.");
}
catch (IOException ex)
{
    Console.WriteLine("Другая ошибка диска: " + ex.Message);
}
Совмещение фильтров и разных типов catch

Здесь мы используем и фильтр, и разделение по типам ошибок. Такая гибкость особенно ценна при разработке сложного приложения, где важно информативно реагировать на частые сбои.

4. Особенности и "подводные камни" фильтров и множественных catch

  • Фильтр catch может использовать переменную исключения (ex), но не может менять её.
  • Если вы бросите исключение внутри when, оно никак не "примется" этим же catch — оно пойдёт дальше вверх по стеку, как будто фильтра и не было.
  • Логика фильтра становится частью контракта: ваш коллега по проекту должен знать, что не всякий IOException будет пойман — только если условие выполнилось.
  • Если вы используете всего один catch на весь метод, но внутри него фильтр, то есть шанс что "лишние" исключения вы не перехватите вовсе. Поэтому, если есть сомнения — используйте явные, отдельные блоки.
  • В больших приложениях можно применять фильтры для централизованной логики, например, логирования только критических ошибок.

5. Сценарии для собеседований и реальной работы

Знание фильтров и множественных catch — не только для красоты или высокого балла по clean code. Это реальный навык, который от вас могут потребовать на собеседованиях, особенно если позиция связана с поддержкой большого, сложного, распределённого приложения.

Примеры вопросов:

  • Как в C# обработать разные сценарии ошибок чтения файла, чтобы для разных ситуаций (нет файла, диск занят, данные испорчены) выводить разное сообщение?
  • Как не "заглушить" важную ошибку, если поставил общий блок catch (Exception)?
  • Для чего нужен фильтр catch? Может ли в нем быть throw?
2
Задача
C# SELF, 13 уровень, 4 лекция
Недоступна
Обработка исключений с использованием множественных catch
Обработка исключений с использованием множественных catch
2
Задача
C# SELF, 13 уровень, 4 лекция
Недоступна
Использование фильтров catch
Использование фильтров catch
1
Опрос
Знакомство с исключениями, 13 уровень, 4 лекция
Недоступен
Знакомство с исключениями
Стек вызовов и создание собственных исключений
Комментарии (2)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Ra Уровень 35 Student
3 октября 2025
Catch when в Java нет, но и не нужен, имхо, условие можно внутри обработать, лишнее усложнение
Александр Уровень 39
29 октября 2025
в шарпе тоже можно внутри кэтча условия обрабатывать, да так и делалось раньше. этот сахарок удобен когда из кода простыня и все блоки сворачиваешь, чтоб глаза не разбегались. ну и типа да, клинкод, вся фигня