Как правильно отлавливать все исключения через фильтр исключений? Фильтры исключений


Фильтры исключений в C# 6.0

Одной из новых возможностей языка C# 6.0 являются фильтры исключений.

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

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

Фильтр исключений логически эквивалентен условию в блоке catch с последующим пробросом исключения, в случае невыполнения условия. Но в случае полноценных фильтров исключений уровня CLR, порядок исполнения будет иным.

Генерация исключения в CLR происходит следующим образом:

1. Идет поиск ближайшего блока catch, который удовлетворяет типу генерируемого исключения.

2. Исполняется предикат фильтра исключения. Если предикат возвращает true, то данный блок catch считается подходящим и начинается раскрутка стека и выполнение всех блоков finally на пути от места генерации исключения к обработчику.

3. Если фильтр исключения возвращает false, то поиск подходящего блока catch продолжается.

Это значит, что порядок исполнения генерации и обработки исключений будет таким:

Сценарии использования

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

  1. Выполнить некоторое действие до раскрутки стека: например, сохранить дамп падающего процесса до вызова блоков finally, закрытия файлов или освобождения блокировок и т.п.

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

  3. Эмуляция блока fault CLR.

У меня ни разу не возникало необходимости в фильтрах исключения для генерации более точных дампов, но команды Roslyn и TypeScript этим пользуются.

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

CLR содержит особый блок обработки исключений под названием fault – аналог блока finally, но который вызывается лишь в случае исключения. Этот блок не может обработать исключение и по его завершению исключение обязательно пробрасывается дальше.

С помощью фильтров исключений можно добавить этого же поведения:

Первый блок catch(Exception) можно рассматривать аналогом блока fault!

В этом случае всегда будет вызываться метод LogException, после чего начнется стандартный поиск нужного блока исключения. Так, в случае генерации InvalidOperationException, оно будет вначале залогировано, а обработано блоком catch(Exception).

Пример с логированием часто приводится в качестве одного из сценариев использования фильтров исключений. Тот же Мэдс Торгесен использует его в статье “New Features in C# 6”. Использовать фильтры исключений для этих целей вполне нормально, но нужно не забывать о правильном порядке блоков catch: первым блоком должен идти catch с фильтром, всегда возвращающим false, ПОСЛЕ которого должны располагаться все остальные блоки catch.

Опасности фильтров исключений

Основная опасность фильтров исключений кроется в ее природе. Поскольку фильтры вызываются до блоков finally, то они вызываются в момент, когда блокировки еще не отпущены, файлы не закрыты, транзакции не завершены и т.д. В большинстве случаев проблем не будет, но отпилить себе ногу все же можно.

Например, генерация исключения из блока lock может легко привести к дедлогку:

Если CanHandle попробует захватить блокировку хитрым образом, то мы получим взаимоблокировку:

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

Фильтры исключений в F#

В каждой второй статье о фильтрах исключений в C# 6.0 говорится, что эта возможность есть также в VB.NET и в F#. К VB.NET претензий нет, а вот в F# фильтров исключений нет. Точнее как, они есть, но их нетJ.

let willThrow() =    try        printfn "throwing..."        failwith "Oops!"    finally        printfn "finally" let check (ex: Exception) =    printfn "check"    true let CheckFilters() =    try        willThrow()    with         | ex when check(ex) -> printfn "caught!"    ()

Если запустить этот код, то вывод на экран будет таким:

throwing...finallycheckCaught!

Фильтры исключений в F# не используют фильтры исключений CLR – это обычное выражение сопоставления с образцом!

sergeyteplyakov.blogspot.com

c# - Почему С# не поддерживает фильтрацию исключений первого шага?

Примечание: это не дубликат вопроса Джеффа.

Этот вопрос спросил: "Это эквивалент?" Я знаю, что нет, и я хочу знать, почему!

Причина, по которой я спрашиваю, это то, что я только что понял, насколько это важно, и вывод кажется мне очень странным.

Блок обработки исключений Microsoft Enterprise Library рекомендует использовать этот шаблон:

catch (Exception x) { if (ExceptionPolicy.HandleException(x, ExceptionPolicies.MyPolicy)) throw; // recover from x somehow }

Политика определяется в XML файле, поэтому это означает, что если у клиента возникла проблема, мы можем изменить политику, чтобы помочь с отслеживанием (или, возможно, скреплением) проблемы, чтобы дать им быстрое разрешение, пока мы не справимся с ним должным образом - что может включать в себя споры с третьими сторонами, о чьей вине это все.

Это в основном признание простого факта, что в реальных приложениях количество исключений и их статус "восстановления" практически невозможно обойти без такого объекта.

Между тем команда CLR в MS говорит, что это не вариант, и, оказывается, эти ребята знают, о чем они говорят! Проблема заключается в том, что перед запуском блока catch выполняется выполнение любых блоков finally, вложенных в блок try. Таким образом, эти блоки finally могут выполнять любое из следующих действий:

  • Безвредно изменить состояние программы (phew, lucky).
  • Извлеките что-то важное в данных клиента, потому что состояние программы прикручено до неизвестной степени.
  • Замаскируйте или уничтожьте важные доказательства того, что нам нужно диагностировать проблему, особенно если мы говорим о вызовах в собственный код.
  • Бросьте еще одно исключение, добавив к общей путанице и страданиям.

Обратите внимание, что дескрипторы using и деструкторы С++/CLI построены на try/finally, поэтому они также затронуты.

Таким образом, шаблон catch/throw для фильтрации исключений не является хорошим. Фактически необходим способ фильтрации исключений посредством политики без их улавливания и, таким образом, запуска выполнения блоков finally, если мы не найдем политику, которая говорит нам, что исключение безопасно восстанавливать.

Команда CLR сообщила об этом недавно:

В результате мы должны написать вспомогательную функцию в VB.NET, чтобы позволить нам получить доступ к этой жизненно важной возможности из С#. Большая проблема в том, что есть проблема в том, что в BCL есть код, который делает это. Многие люди писали об этом, но редко упоминают о блоках try/finally, которые являются убийцами.

Что я хотел бы знать:

  • Есть ли публичные заявления или прямые письма, которые люди получили от команды С# по этому вопросу?
  • Есть ли какие-либо предложения Microsoft Connect, требующие этого? Я слышал слухи о них, но ни один из вероятных ключевых слов не появился.

Обновление:, как указано выше, я уже искал в Microsoft Connect, ничего не обнаружив. У меня также (неудивительно) Googled. Я только нашел людей объясняя, зачем им нужна эта функция или указывая преимущества этого в VB.NET или бесплодно надеясь, что он будет добавлен в будущую версию С#, или обходится вокруг, и много вводящие в заблуждение советы, Но никаких утверждений об обосновании отказа от нее из всех текущих версий С#. И причина, по которой я спрашиваю о существующих проблемах Connect, заключается в том, что (а) я не создаю ненужный дубликат и (б) я могу рассказать заинтересованным лицам, если мне нужно его создать.

Обновление 2: найдено интересное старое сообщение в блоге от Эрика Гуннерсона, ранее из команды С#:

"Да, возможность поставить условие на улов несколько более удобен чем писать тест самостоятельно, но это действительно не позволяет вы должны сделать что-нибудь новое".

Это было то же самое предположение, что и у меня, до тех пор, пока оно не было должным образом объяснено мне.

qaru.site

Использование обработчиков исключений с пользовательской фильтрацией

  • 03/30/2017
  • Время чтения: 2 мин
  • Соавторы

В этой статье

Сейчас Visual Basic поддерживает исключения с пользовательской фильтрацией.Currently, Visual Basic supports user-filtered exceptions. Обработчики исключений с пользовательской фильтрацией перехватывают и обрабатывают исключения с учетом определенных для исключения требований.User-filtered exception handlers catch and handle exceptions based on requirements you define for the exception. Эти обработчики используют инструкцию Catch с ключевым словом When.These handlers use the Catch statement with the When keyword.

Этот метод удобен, когда конкретный объект исключения соответствует нескольким ошибкам.This technique is useful when a particular exception object corresponds to multiple errors. В этом случае у объекта обычно есть свойство, которое содержит код ошибки.In this case, the object typically has a property that contains the specific error code associated with the error. С помощью кода ошибки можно выбрать конкретную ошибку, которую вы хотите обработать в инструкции Catch.You can use the error code property in the expression to select only the particular error you want to handle in that Catch clause.

В следующем примере Visual Basic демонстрируются инструкции Catch/When.The following Visual Basic example illustrates the Catch/When statement.

Try 'Try statements. Catch When Err = VBErr_ClassLoadException 'Catch statements. End Try

Выражение для предложения пользовательской фильтрации не ограничено.The expression of the user-filtered clause is not restricted in any way. При возникновении исключения в выражении с пользовательской фильтрацией это исключение отбрасывается, а выражение фильтрации считается равным false.If an exception occurs during execution of the user-filtered expression, that exception is discarded and the filter expression is considered to have evaluated to false. В этом случае среда CLR продолжает поиск обработчика для текущего исключения.In this case, the common language runtime continues the search for a handler for the current exception.

Объединение конкретного исключения и условий пользовательской фильтрацииCombining the Specific Exception and the User-Filtered Clauses

Оператор catch может содержать как конкретное исключение, так и условия пользовательской фильтрации.A catch statement can contain both the specific exception and the user-filtered clauses. Сначала среда выполнения проверяет конкретное исключение.The runtime tests the specific exception first. Если проверка пройдена, среда выполнения выполняет фильтр пользователя.If the specific exception succeeds, the runtime executes the user filter. Общий фильтр может содержать ссылку на переменную, объявленную в классе фильтра.The generic filter can contain a reference to the variable declared in the class filter. Обратите внимание, что порядок двух выражений фильтра изменить нельзя.Note that the order of the two filter clauses cannot be reversed.

В следующем примере Visual Basic демонстрируется конкретное исключение ClassLoadException в инструкции Catch, а также предложение для пользовательской фильтрации с использованием ключевого слова When.The following Visual Basic example shows the specific exception ClassLoadException in the Catch statement as well as the user-filtered clause using the When keyword.

Try 'Try statements. Catch cle As ClassLoadException When cle.IsRecoverable() 'Catch statements. End Try

См. такжеSee also

docs.microsoft.com

Как правильно отлавливать все исключения через фильтр исключений? — Toster.ru

Всем привет!

Пытаюсь отлавливать все исключения в web api контролахС этой целью написал такой фильтр

public class NotImplExceptionFilterAttribute : ExceptionFilterAttribute { public override void OnException(HttpActionExecutedContext filterContext) { // Вот тут записываем в лог информацию об исключении } }

Проблема заключается в том, что само исключение почему то теряется. В обьекте filterContext.Exception всегда содержится вот что:

Message = Object reference not set to an instance of an object. Data = System.Collections.ListDictionaryInternal InnerException = TargetSite = Void Log(System.Web.Http.Filters.HttpActionExecutedContext) StackTrace = at MyApplication.Filters.WebApiLogFIlter.Log(HttpActionExecutedContext actionExecutedContext) at MyApplication.Filters.WebApiLogFIlter.OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken) at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Filters.ActionFilterAttribute.<ExecuteActionFilterAsyncCore>d__0.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Filters.AuthorizationFilterAttribute.<ExecuteAuthorizationFilterAsyncCore>d__2.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Controllers.ExceptionFilterResult.<ExecuteAsync>d__0.MoveNext() HelpLink = Source = MyApplication HResult = -2147467261

Что я делаю не так, и как получить исключение в фильтре исключений?

toster.ru