Я столкнулся с этой новой функцией C #, которая позволяет обработчику catch выполняться при выполнении определенного условия.
int i = 0;
try
{
throw new ArgumentNullException(nameof(i));
}
catch (ArgumentNullException e)
when (i == 1)
{
Console.WriteLine("Caught Argument Null Exception");
}
Я пытаюсь понять, когда это может быть полезно.
Один сценарий может быть примерно таким:
try
{
DatabaseUpdate()
}
catch (SQLException e)
when (driver == "MySQL")
{
//MySQL specific error handling and wrapping up the exception
}
catch (SQLException e)
when (driver == "Oracle")
{
//Oracle specific error handling and wrapping up of exception
}
..
но это снова то, что я могу делать в одном обработчике и делегировать разным методам в зависимости от типа драйвера. Делает ли это код более понятным? Возможно, нет.
Другой сценарий, который я могу придумать, выглядит примерно так:
try
{
SomeOperation();
}
catch(SomeException e)
when (Condition == true)
{
//some specific error handling that this layer can handle
}
catch (Exception e) //catchall
{
throw;
}
Опять же, это то, что мне нравится:
try
{
SomeOperation();
}
catch(SomeException e)
{
if (condition == true)
{
//some specific error handling that this layer can handle
}
else
throw;
}
Ускоряет ли использование функции «поймать, когда» обработку исключений, потому что обработчик пропускается как таковой, а разворачивание стека может произойти намного раньше, чем при обработке конкретных вариантов использования в обработчике? Существуют ли какие-либо конкретные варианты использования, которые лучше подходят для этой функции, которые люди могут затем принять в качестве передовой практики?
источник
when
необходимо получить доступ к самому исключениюtry..catch...catch..catch..finally
?catch (Exception ex)
, проверить тип иthrow
иначе. Чуть более организованный код (он же избегание шума кода) - вот почему существует эта функция. (На самом деле это верно для многих функций.)Ответы:
Блоки перехвата уже позволяют фильтровать по типу исключения:
В
when
позволяет расширить этот фильтр на общие выражения.Таким образом, вы используете
when
предложение для случаев, когда тип исключения недостаточно различен, чтобы определить, следует ли здесь обрабатывать исключение или нет.Типичный вариант использования - это типы исключений, которые на самом деле являются оболочкой для множества различных типов ошибок.
Вот случай, который я действительно использовал (в VB, который уже довольно давно имеет эту функцию):
То же самое для
SqlException
, у которого также естьErrorCode
свойство. Альтернативой может быть что-то вроде этого:который, возможно, менее элегантен и немного нарушает трассировку стека .
Кроме того, вы можете дважды упомянуть один и тот же тип исключения в одном блоке try-catch:
что было бы невозможно без
when
условия.источник
catch
, не так ли?when
позволяет обрабатывать один и тот же тип исключения несколько раз. Вы должны упомянуть и об этом, поскольку это принципиальная разница. Без этогоwhen
вы получите ошибку компилятора.Из вики Roslyn (выделено мной):
Первый пункт стоит продемонстрировать.
Если мы запустим это в WinDbg до тех пор, пока не произойдет исключение, и распечатаем стек, используя,
!clrstack -i -a
мы увидим только кадрA
:Однако, если мы изменим программу для использования
when
:Мы увидим, что стек также содержит
B
фрейм:Эта информация может быть очень полезна при отладке аварийных дампов.
источник
throw;
(в противоположностьthrow ex;
)? +1 за побочный эффект. Не уверен, что одобряю это, но хорошо знать об этой технике.throw;
, стек раскручивается, и вы теряете значения параметров.throw;
это немногоthrow ex;
меняет трассировку стека и сильно меняет.throw
немного нарушает трассировку стека. Номера строк при использовании отличаютсяthrow
отwhen
.Когда генерируется исключение, первый проход обработки исключения определяет, где будет обнаружено исключение, прежде чем раскручивать стек; если / когда местоположение «улова» идентифицировано, все блоки «finally» запускаются (обратите внимание, что если исключение выходит из блока «finally», обработка более раннего исключения может быть прекращена). Как только это произойдет, код возобновит выполнение на «ловушке».
Если в функции есть точка останова, которая оценивается как часть «когда», эта точка останова приостанавливает выполнение до того, как произойдет какое-либо раскручивание стека; Напротив, точка останова на «перехвате» приостановит выполнение только после того, как все
finally
обработчики будут запущены.Наконец, если строки 23 и 27
foo
вызоваbar
и вызов в строке 23 генерируют исключение, которое перехватывается внутриfoo
и повторно генерируется в строке 57, тогда трассировка стека будет предполагать, что исключение произошло при вызовеbar
из строки 57 [местоположение повторного вызова] , уничтожая любую информацию о том, произошло ли исключение в вызове линии 23 или линии 27. Использование,when
чтобы избежать перехвата исключения, в первую очередь позволяет избежать такого нарушения.Кстати, полезный шаблон, который досадно неудобен как в C #, так и в VB.NET, заключается в использовании вызова функции в
when
предложении для установки переменной, которая может использоваться вfinally
предложении, чтобы определить, нормально ли завершилась функция, для обработки случаев, когда функция не имеет надежды «разрешить» возникшее исключение, но, тем не менее, должен принять меры на его основе. Например, если в фабричном методе генерируется исключение, которое должно возвращать объект, инкапсулирующий ресурсы, любые ресурсы, которые были получены, необходимо будет освободить, но базовое исключение должно распространяться до вызывающего. Самый чистый способ справиться с этим семантически (хотя и не синтаксически) - это иметьfinally
block проверяет, произошло ли исключение, и, если да, освобождает все ресурсы, полученные от имени объекта, который больше не будет возвращаться. Поскольку код очистки не имеет никакой надежды на разрешение любого условия, вызвавшего исключение, на самом деле он не долженcatch
этого делать, а просто должен знать, что произошло. Вызов такой функции, как:внутри
when
предложения позволит фабричной функции узнать, что что-то произошло.источник