Как перехватить несколько типов исключений в одном блоке catch в C#?
Решение: перехват нескольких исключений в C#
В C# нет прямого синтаксиса catch (ExType1 | ExType2 ex), как в Java. Однако есть несколько элегантных способов решить вашу проблему. Самый простой и рекомендуемый — использовать базовый класс исключения или паттерн фильтрации исключений (доступен с C# 6.0 / .NET 4.6+).
Способ 1: Использование базового класса (если логика одинаковая)
Если все исключения, которые вы хотите обработать, наследуются от общего предка (например, SystemException или Exception), можно просто поймать его. Но будьте осторожны — это может скрыть неожиданные ошибки.
try
{
// код
}
catch (Exception ex) when (ex is FileNotFoundException
|| ex is IOException
|| ex is SqlException
|| ex is ArgumentException)
{
LogError(ex);
ShowMessage("Произошла ошибка: " + ex.Message);
}
Этот код использует фильтр исключений (when). Он появился в C# 6.0. Фильтр работает эффективнее, чем блок catch с последующей проверкой типа, потому что исключение не перехватывается, если условие ложно.
Способ 2: Использование паттерна switch (C# 7.0+)
Если вам нужно не только перехватить, но и выполнить разную логику для разных типов, используйте catch (Exception ex) и внутри — switch с типами:
try
{
// код
}
catch (Exception ex)
{
switch (ex)
{
case FileNotFoundException:
LogError(ex);
ShowMessage("Файл не найден");
break;
case IOException:
LogError(ex);
ShowMessage("Ошибка ввода-вывода");
break;
case SqlException:
LogError(ex);
ShowMessage("Ошибка базы данных");
break;
case ArgumentException:
LogError(ex);
ShowMessage("Неверный аргумент");
break;
default:
throw; // пробрасываем неожиданные исключения
}
}
Способ 3: Общий блок catch с повторным выбрасыванием
Если вам действительно нужно одинаковое действие для всех перечисленных типов, можно объединить их в один фильтр, как в первом способе. Это самый компактный вариант:
try
{
// код
}
catch (Exception ex) when (ex is FileNotFoundException
or IOException
or SqlException
or ArgumentException)
{
// Обработка
}
Обратите внимание: when принимает логическое выражение. Вы можете использовать or (C# 9.0+) или старый синтаксис ||.
Важные рекомендации
- Не ловите
Exceptionбез фильтра — это скроет критические ошибки (например,OutOfMemoryException,StackOverflowException). - Используйте фильтр
when— он предотвращает перехват исключения, если условие ложно, что позволяет сохранить стек вызовов. - Если вы используете .NET Framework 4.7.2, у вас есть доступ к C# 7.x, поэтому все способы выше работают.
Выберите тот вариант, который лучше подходит под вашу задачу. Лично я рекомендую фильтр исключений — он лаконичен и эффективен.
<<Альтернативный подход: вынос логики в метод
Если вам не нравится синтаксис с фильтрами или вы используете старую версию C# (до 6.0), можно вынести повторяющийся код в отдельный метод и перехватывать исключения по одному, но вызывать один и тот же обработчик:
private void HandleException(Exception ex, string message)
{
LogError(ex);
ShowMessage(message);
}
try
{
// код
}
catch (FileNotFoundException ex)
{
HandleException(ex, "Файл не найден");
}
catch (IOException ex)
{
HandleException(ex, "Ошибка ввода-вывода");
}
catch (SqlException ex)
{
HandleException(ex, "Ошибка базы данных");
}
catch (ArgumentException ex)
{
HandleException(ex, "Неверный аргумент");
}
Этот способ не сокращает количество блоков catch, но уменьшает дублирование кода. Он подходит для случаев, когда логика обработки действительно одинаковая, но сообщения пользователю различаются.
Ещё один вариант — использовать общий обработчик через событие AppDomain.CurrentDomain.FirstChanceException (для глобального логирования), но это уже экзотика и не рекомендуется для простых сценариев.