День двести пятьдесят третий. #ЗаметкиНаПолях
Фильтры исключений
Представьте, что вы выполняете веб-операцию и знаете, что сервер, к которому вы подключаетесь, иногда недоступен. Если вам не удаётся подключиться к нему, то у вас есть запасной вариант, однако любой другой вид сбоя должен привести к обычному выбросу исключения. До C #6 нужно было поймать исключение и выбросить его повторно, если статус отличался от нужного:
1. Перехват одного исключения несколько раз
В прошлом всегда было ошибкой указывать один и тот же тип исключения в нескольких блоках
2. Повторные попытки
Облачные вычисления становятся все более распространенными, и мы, как правило, всё больше узнаём об операциях, которые могут привести к сбою, и о задумываемся о том, как обрабатывать сбои в нашем коде. При удаленных операциях - например, вызовах удалённых веб-служб и баз данных - иногда возникают временные сбои, и можно безопасно повторить попытку. Вот как это можно реализовать, используя фильтры исключений:
Иногда полезно записывать исключение в лог, даже если оно будет перехвачено на более верхних уровнях. Вы можете использовать для этого фильтры исключений, чтобы не нарушать поток выполнения. Всё, что вам нужно, это фильтр исключений, который вызывает метод для записи в лог и возвращает
Фильтры исключений
Представьте, что вы выполняете веб-операцию и знаете, что сервер, к которому вы подключаетесь, иногда недоступен. Если вам не удаётся подключиться к нему, то у вас есть запасной вариант, однако любой другой вид сбоя должен привести к обычному выбросу исключения. До C #6 нужно было поймать исключение и выбросить его повторно, если статус отличался от нужного:
try {…}Используя фильтры исключений, если вы не хотите обрабатывать исключение, можно не перехватывать его, а отфильтровать сразу в блоке
catch (WebException e)
{
if (e.Status != WebExceptionStatus.ConnectFailure)
{
throw;
}
…
}
catch
: try {…}Варианты использования:
catch (WebException e)
when (e.Status == WebExceptionStatus.ConnectFailure) {…}
1. Перехват одного исключения несколько раз
В прошлом всегда было ошибкой указывать один и тот же тип исключения в нескольких блоках
catch
для одного и того же блока try
. Это не имело смысла, потому что второй блок никогда не достигался. С фильтрами исключений ситуация изменилась. Предположим, вы извлекаете веб-контент на основе URL-адреса, предоставленного пользователем. Возможно, вы захотите обработать сбой соединения одним способом, сбой разрешения домена другим способом и не обрабатывать другие типы сбоев:try {…}Если вы хотите обработать все другие исключения WebException на том же уровне, можно добавить общий блок
catch (WebException e)
when (e.Status == WebExceptionStatus.ConnectFailure) {…}
catch (WebException e)
when (e.Status == WebExceptionStatus.NameResolutionFailure) {…}
catch (WebException e) {…}
без фильтра последним пунктом.2. Повторные попытки
Облачные вычисления становятся все более распространенными, и мы, как правило, всё больше узнаём об операциях, которые могут привести к сбою, и о задумываемся о том, как обрабатывать сбои в нашем коде. При удаленных операциях - например, вызовах удалённых веб-служб и баз данных - иногда возникают временные сбои, и можно безопасно повторить попытку. Вот как это можно реализовать, используя фильтры исключений:
static T Retry<T>(Func<T> operation, int attempts)3. Запись в лог, как побочный эффект
{
while (true)
{
try
{
attempts--;
return operation();
}
catch (Exception e) when (attempts > 0)
{
Console.WriteLine($"Неудача: {e}");
Console.WriteLine($"Осталось попыток: {attempts}");
Thread.Sleep(5000);
}
}
}
Иногда полезно записывать исключение в лог, даже если оно будет перехвачено на более верхних уровнях. Вы можете использовать для этого фильтры исключений, чтобы не нарушать поток выполнения. Всё, что вам нужно, это фильтр исключений, который вызывает метод для записи в лог и возвращает
false
, чтобы указать, что вы не хотите перехватывать это исключение:try {…}Источник: Jon Skeet “C# In Depth”. 4th ed – Manning Publications Co, 2019. Глава 10.
catch (Exception e) when (Log(e)) {…}
…
static bool Log(Exception e)
{
Console.WriteLine($"{DateTime.UtcNow}: {e.GetType()} {e.Message}");
return false;
}