Блок catch и фильтры исключений


Определение блока catch
За обработку исключения отвечает блок catch, который может иметь следующие формы:

catch
{
// выполняемые инструкции
}
Обрабатывает любое исключение, которое возникло в блоке try. Выше уже был продемонстрирован пример подобного блока.

catch (тип_исключения)
{
// выполняемые инструкции
}
Обрабатывает только те исключения, которые соответствуют типу, указаному в скобках после оператора catch.

Например, обработаем только исключения типа DivideByZeroException:

try
{
int x = 5;
int y = x / 0;
Console.WriteLine($"Результат: {y}");
}
catch(DivideByZeroException)
{
Console.WriteLine("Возникло исключение DivideByZeroException");
}
Однако если в блоке try возникнут исключения каких-то других типов, отличных от DivideByZeroException, то они не будут обработаны.

catch (тип_исключения имя_переменной)
{
// выполняемые инструкции
}
Обрабатывает только те исключения, которые соответствуют типу, указаному в скобках после оператора catch. А вся информация об исключении помещается в переменную данного типа. Например:

try
{
int x = 5;
int y = x / 0;
Console.WriteLine($"Результат: {y}");
}
catch(DivideByZeroException ex)
{
Console.WriteLine($"Возникло исключение {ex.Message}");
}
Фактически этот случай аналогичен предыдущему за тем исключением, что здесь используется переменная. В данном случае в переменную ex, которая представляет тип DivideByZeroException, помещается информация о возникшем исключени. И с помощью свойства Message мы можем получить сообщение об ошибке.

Если нам не нужна информация об исключении, то переменную можно не использовать как в предыдущем случае.

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

catch when(условие)
{

}
В этом случае обработка исключения в блоке catch производится только в том случае, если условие в выражении when истинно. Например:

int x = 1;
int y = 0;

try
{
int result1 = x / y;
int result2 = y / x;
}
catch (DivideByZeroException) when (y == 0)
{
Console.WriteLine("y не должен быть равен 0");
}
catch(DivideByZeroException ex)
{
Console.WriteLine(ex.Message);
}
В данном случае будет выброшено исключение, так как y=0. Здесь два блока catch, и оба они обрабатывают исключения типа DivideByZeroException, то есть по сути все исключения, генерируемые при делении на ноль. Но поскольку для первого блока указано условие y == 0, то именно этот блок будет обрабатывать данное исключение - условие, указанное после оператора when возвращает true.

Противоположная ситуация:

int x = 0;
int y = 1;

try
{
int result1 = x / y;
int result2 = y / x;
}
catch (DivideByZeroException) when (y == 0)
{
Console.WriteLine("y не должен быть равен 0");
}
catch(DivideByZeroException ex)
{
Console.WriteLine(ex.Message);
}
В данном случае будет выброшено исключение, так как x=0. Условие первого блока catch - y == 0 теперь возвращает false. Поэтому CLR будет дальше искать соответствующие блоки catch далее и для обработки исключения выберет второй блок catch. В итоге если мы уберем второй блок catch, то исключение вобще не будет обрабатываться.