Не рекомендуется просто ловить System.Exception
. Вместо этого должны быть обнаружены только «известные» исключения.
Теперь это иногда приводит к ненужному повторяющемуся коду, например:
try
{
WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
WebId = Guid.Empty;
}
catch (OverflowException)
{
WebId = Guid.Empty;
}
Интересно: есть ли способ перехватить оба исключения и позвонить только WebId = Guid.Empty
один раз?
Данный пример довольно прост, так как это всего лишь GUID
. Но представьте себе код, в котором вы несколько раз модифицируете объект, и если одна из манипуляций не удалась ожидаемым образом, вы хотите «сбросить» object
. Однако, если есть непредвиденное исключение, я все же хочу бросить это выше.
c#
.net
exception
exception-handling
Майкл Стум
источник
источник
AggregateException
: добавление AggregateException в мой собственный кодОтветы:
Поймай
System.Exception
и включи типыисточник
РЕДАКТИРОВАТЬ: я согласен с другими, которые говорят, что, начиная с C # 6.0, фильтры исключений теперь являются идеальным способом:
catch (Exception ex) when (ex is ... || ex is ... )
За исключением того, что я все еще ненавижу макет с одной строкой и лично выложу код, как показано ниже. Я думаю, что это так же функционально, как и эстетично, поскольку я считаю, что это улучшает понимание. Некоторые могут не согласиться:
ОРИГИНАЛ:
Я знаю, что немного опоздал на вечеринку здесь, но святой дым ...
Переходя к погоне, этот вид дублирует более ранний ответ, но если вы действительно хотите выполнить общее действие для нескольких типов исключений и сохранить все в чистоте и порядке в рамках одного метода, почему бы просто не использовать лямбду / Закрытие / встроенная функция, чтобы сделать что-то вроде следующего? Я имею в виду, что вполне вероятно, что в итоге вы поймете, что просто хотите сделать это закрытие отдельным методом, который вы можете использовать повсюду. Но тогда это будет очень легко сделать без реального структурного изменения остальной части кода. Правильно?
Я не могу не задаться вопросом ( предупреждение: впереди немножко иронии / сарказма), зачем вообще все эти усилия просто заменять следующим:
... с каким-то безумным изменением этого следующего запаха кода, я имею в виду пример, только чтобы сделать вид, что вы экономите несколько нажатий клавиш.
Потому что это, конечно, не более автоматически читается.
Конечно, я оставил три идентичных экземпляра
/* write to a log, whatever... */ return;
из первого примера.Но это своего рода моя точка зрения. Вы все слышали о функциях / методах, верно? Шутки в сторону. Напишите общую
ErrorHandler
функцию и, например, вызывайте ее из каждого блока catch.Если вы спросите меня, второй пример (с
if
иis
ключевыми словами) одновременно значительно менее читаемый, и одновременно значительно более подвержены ошибкам во время фазы вашего проекта технического обслуживания.Фаза обслуживания для тех, кто может быть относительно новичком в программировании, будет составлять 98,7% или более от общего срока службы вашего проекта, и бедняга, выполняющий обслуживание, почти наверняка будет кем-то другим, кроме вас. И есть очень хороший шанс, что они будут тратить 50% своего времени на работу, ругаясь на ваше имя.
И, конечно же FxCop лает на вас , и поэтому вы должны также добавить атрибут в свой код , который точно пронестись делать с запущенной программой, и только там , чтобы сказать FxCop игнорировать вопрос , что в 99,9% случаев он полностью правильно в маркировке. И, извините, я могу ошибаться, но разве этот атрибут «игнорировать» не скомпилирован в ваше приложение?
Поможет ли размещение всего
if
теста в одной строке сделать его более читабельным? Я так не думаю. Я имею в виду, у меня когда-то был другой программист, яростно споривший однажды, что размещение большего количества кода в одной строке заставит его «работать быстрее». Но, конечно, он был совершенно безумным. Попытка объяснить ему (с открытым лицом - что было непросто), как интерпретатор или компилятор разбил бы эту длинную строку на дискретные операторы по одной инструкции на строку - по сути, идентичные результату, если бы он пошел вперед и просто сделал код читабельным вместо того, чтобы пытаться перехитрить компилятор - не оказал на него никакого влияния. Но я отвлекся.Насколько менее читабельным это становится, если добавить еще три типа исключений через месяц или два? (Ответ: это становится намного менее читабельным).
Один из главных моментов, на самом деле, заключается в том, что основная часть форматирования текстового исходного кода, на который мы все смотрим каждый день, состоит в том, чтобы сделать его действительно, действительно очевидным для других людей, что на самом деле происходит, когда код выполняется. Потому что компилятор превращает исходный код во что-то совершенно другое и не заботится о вашем стиле форматирования кода. Так что все на одной линии - полный отстой.
Просто говорю...
источник
Exception
s и проверить тип. Я думал, что это очистило код, но что-то заставляло меня возвращаться к вопросу, и я фактически читал другие ответы на вопрос. Я жевал это некоторое время, но я должен согласиться с вами. Это более читаемым и ремонтопригодны использовать функцию сушки ваш код , чем поймать все, проверьте тип сравнения со списком, код оберточной и метания. Спасибо за опоздание и предоставление альтернативного и вменяемого (IMO) варианта. +1.throw;
. Вам придется повторять эту строку кода в каждом блоке catch (очевидно, это не конец света, но стоит упомянуть, поскольку это код, который нужно будет повторять).throw();
оператор в каждом блоке улова - это небольшая цена, IMO, и все равно оставляет вам возможность выполнить дополнительную очистку для конкретного типа исключения, если это необходимо.Func<Exception, MyEnumType>
вместоAction<Exception>
. То естьFunc<T, Result>
сResult
типом возврата.Как уже отмечали другие, вы можете иметь
if
оператор внутри блока catch, чтобы определить, что происходит. C # 6 поддерживает фильтры исключений, поэтому будет работать следующее:Затем
MyFilter
метод может выглядеть примерно так:В качестве альтернативы, все это можно сделать встроенным (правая часть оператора when просто должна быть логическим выражением).
Это отличается от использования
if
оператора внутриcatch
блока, использование фильтров исключений не размотает стек.Вы можете скачать Visual Studio 2015, чтобы проверить это.
Если вы хотите продолжить использовать Visual Studio 2013, вы можете установить следующий пакет nuget:
На момент написания, это будет включать поддержку C # 6.
источник
К сожалению, не в C #, поскольку для этого вам понадобится фильтр исключений, а C # не предоставляет эту возможность MSIL. VB.NET имеет такую возможность, например,
Что вы можете сделать, это использовать анонимную функцию для инкапсуляции вашего кода ошибки, а затем вызывать его в этих определенных блоках перехвата:
источник
throw e;
уничтожает stacktrace и callstack,throw;
уничтожает "only" callstack (делая неработоспособными аварийные дампы!). Очень хорошая причина, чтобы использовать ни то, ни другое, если этого можно избежать!Для полноты, начиная с .NET 4.0 код можно переписать так:
TryParse никогда не генерирует исключения и возвращает false, если формат неверный, устанавливая WebId в
Guid.Empty
.Начиная с C # 7, вы можете избежать введения переменной в отдельной строке:
Вы также можете создать методы для анализа возвращаемых кортежей, которые еще не доступны в .NET Framework начиная с версии 4.6:
И используйте их так:
Следующее бесполезное обновление этого бесполезного ответа происходит, когда деконструкция out-параметров реализована в C # 12. :)
источник
Guid.TryParse
никогда не вернетсяGuid.Empty
. Если строка имеет неправильный формат, она устанавливаетresult
выходной параметр в значениеGuid.Empty
, но возвращаетfalse
. Я упоминаю об этом, потому что я видел код, который делает вещи в стилеGuid.TryParse(s, out guid); if (guid == Guid.Empty) { /* handle invalid s */ }
, который обычно неверен, еслиs
может быть строковым представлениемGuid.Empty
.if( Guid.TryParse(s, out guid){ /* success! */ } else { /* handle invalid s */ }
, который не оставляет двусмысленности, как в сломанном примере, где входное значение может фактически быть строковым представлением Guid.Фильтры исключений теперь доступны в c # 6+. Ты можешь сделать
В C # 7.0+ вы можете комбинировать это с сопоставлением с шаблоном
источник
Если вы можете обновить приложение до C # 6, вам повезет. В новой версии C # реализованы фильтры исключений. Таким образом, вы можете написать это:
Некоторые люди думают, что этот код такой же, как
Но это не так. На самом деле это единственная новая функция в C # 6, которую невозможно эмулировать в предыдущих версиях. Во-первых, повторный бросок означает больше накладных расходов, чем пропуск улова. Во-вторых, это не семантически эквивалентно. Новая функция сохраняет стек без изменений при отладке кода. Без этой функции аварийный дамп менее полезен или даже бесполезен.
Смотрите обсуждение этого на CodePlex . И пример, показывающий разницу .
источник
Если вы не хотите использовать
if
заявление в пределахcatch
областей, вC# 6.0
вы можете использоватьException Filters
синтаксис , который уже был поддержан СЕК в превью версии , но существовала только вVB.NET
/MSIL
:Этот код будет ловить
Exception
только когда этоInvalidDataException
илиArgumentNullException
.На самом деле, вы можете поместить в это
when
предложение практически любое условие :Обратите внимание, что в отличие от
if
оператора внутри областиcatch
видимости, онException Filters
не может выдаватьExceptions
, и когда он это делает, или когда условие неtrue
выполняетсяcatch
, вместо него будет оцениваться следующее условие:Когда будет больше одного
true
Exception Filter
- будет принято первое:И, как вы можете видеть из
MSIL
этого, код не переводится вif
операторы, а вFilters
иExceptions
не может быть выброшен из областей, отмеченных значкомFilter 1
и,Filter 2
но фильтр, выбрасывающий значениеException
, потерпит неудачу, а также последнее значение сравнения, помещенное в стек передendfilter
командой определит успех / неудачу фильтра (Catch 1
XORCatch 2
будет выполняться соответственно):Также конкретно
Guid
естьGuid.TryParse
метод.источник
С C # 7 ответ от Michael Stum может быть улучшен при сохранении читабельности оператора switch:
И с C # 8 в качестве выражения переключения:
источник
when
гораздо более элегантно / уместно, чем переключение.catch
блок, или вам все равно придется его приводить .. По моему опыту,throw;
в вашемcatch
блоке, вероятно, запах кода.Принятый ответ кажется приемлемым, за исключением того, что CodeAnalysis / FxCop будет жаловаться на тот факт, что он перехватывает общий тип исключения.
Кроме того, кажется, что оператор is может слегка ухудшить производительность.
CA1800: не приводите излишне призывающие «рассмотреть возможность проверки результата оператора« as »вместо этого», но если вы сделаете это, вы будете писать больше кода, чем если бы вы ловили каждое исключение отдельно.
Во всяком случае, вот что я бы сделал:
источник
is
оператора может оказать небольшое негативное влияние на производительность, также верно, что обработчик исключений не место, чтобы чрезмерно беспокоиться об оптимизации производительности. Если ваше приложение тратит так много времени на обработчики исключений, что оптимизация производительности может реально повлиять на производительность приложения, то есть другие проблемы с кодом, на которые следует обратить пристальное внимание. Сказав это, мне все еще не нравится это решение, потому что вы теряете трассировку стека и потому что очистка контекстуально удаляется из оператора catch.is
оператор снижает производительность, это если вы позже выполняетеas
операцию (следовательно, они необоснованно квалифицируют правило ). Если все, что вы делаете, это тестирование приведения без необходимости его выполнения, тоis
оператор - именно то, что вы хотите использовать.в C # 6 рекомендуется использовать фильтры исключений, вот пример:
источник
Это вариант ответа Мэтта (я чувствую, что это немного чище) ... используйте метод:
Любые другие исключения будут выброшены, и код
WebId = Guid.Empty;
не будет поражен. Если вы не хотите, чтобы другие исключения вызывали сбой вашей программы, просто добавьте это ПОСЛЕ двух других зацепок:источник
WebId = Guid.Emtpy
в случае, если не было сгенерировано исключение.return
к своему ответу. Спасибо за вклад.Ответ Джозефа Дейгла - хорошее решение, но я обнаружил, что следующая структура немного более аккуратна и менее подвержена ошибкам.
Есть несколько преимуществ обращения выражения:
Его можно даже сжать в одну строку (хотя и не очень красиво)
Edit: фильтрация исключений в C # 6.0 будет сделать синтаксис немного чище и поставляется с рядом других преимуществ над любым текущим решением. (прежде всего оставляя стек без повреждений)
Вот как будет выглядеть та же проблема с использованием синтаксиса C # 6.0:
источник
return
, хотя инвертировать условие тоже немного лучше.@Micheal
Немного переработанная версия вашего кода:
Сравнение строк уродливо и медленно.
источник
throw new FormatException();
с ,throw new NewlyDerivedFromFormatException();
не нарушая код , используя библиотеку, и это будет справедливо для всех обработки исключений , кроме случаев , когда кто - то использовал==
вместоis
(или простоcatch (FormatException)
).Как насчет
источник
источник
Предостережение и предупреждение: еще один вид, функциональный стиль.
То, что находится в ссылке, не дает прямого ответа на ваш вопрос, но тривиально расширить его так:
(В основном, обеспечивает другую пустую
Catch
перегрузку, которая возвращает себя)Большой вопрос к этому - почему . Я не думаю, что стоимость перевешивает выигрыш здесь :)
источник
Обновление 2015-12-15: см. Https://stackoverflow.com/a/22864936/1718702 для C # 6. Это чище и теперь стандарт в языке.
Предназначенный для людей, которые хотят, чтобы более элегантное решение ловило один раз и фильтровало исключения, я использую метод расширения, как показано ниже.
У меня уже было это расширение в моей библиотеке, изначально написанное для других целей, но оно отлично работало для
type
проверки исключений. Плюс, имхо, это выглядит чище, чем куча||
утверждений. Кроме того, в отличие от принятого ответа, я предпочитаю явную обработку исключений, поэтомуex is ...
имел нежелательное поведение, так как классы-наследники могут назначаться родительским типам).Применение
Расширение IsAnyOf.cs (см. Пример полной обработки ошибок для зависимостей)
Пример полной обработки ошибок (копирование-вставка в новое консольное приложение)
Два примера модульных тестов NUnit
Поведение соответствия для
Exception
типов является точным (т. Е. Дочерний элемент НЕ соответствует ни одному из его родительских типов).источник
when
, как и любая версияcatch (Exception ex) {if (...) {/*handle*/} throw;}
. Реальное значениеwhen
состоит в том, что фильтр запускается до того, как исключение было перехвачено , что позволяет избежать повреждения затрат / стека при повторном выбросе. Он использует функцию CLR, которая ранее была доступна только для VB и MSIL.IsAnyOf
метод можно переписать так же простоp_comparisons.Contains(p_parameter)
Поскольку я чувствовал, что эти ответы только коснулись поверхности, я попытался копнуть немного глубже.
Итак, что мы действительно хотим сделать, это то, что не компилируется, скажем:
Причина, по которой мы этого хотим, заключается в том, что мы не хотим, чтобы обработчик исключений перехватывал то, что нам нужно позже в процессе. Конечно, мы можем поймать исключение и проверить, что делать, если «да», но давайте будем честными, мы этого не хотим. (FxCop, проблемы с отладчиком, уродство)
Так почему же этот код не скомпилируется - и как мы можем взломать его таким образом, чтобы он был?
Если мы посмотрим на код, то, что мы действительно хотели бы сделать, это переадресовать вызов. Однако, согласно MS Partition II, блоки обработчиков исключений IL не будут работать таким образом, что в этом случае имеет смысл, поскольку это подразумевает, что объект «исключения» может иметь разные типы.
Или, чтобы написать это в коде, мы просим компилятор сделать что-то вроде этого (ну, это не совсем правильно, но я думаю, это самая близкая вещь):
Причина, по которой это не скомпилируется, совершенно очевидна: какой тип и значение будет иметь объект «$ exception» (которые здесь хранятся в переменных «e»)? Мы хотим, чтобы компилятор справился с этим, отметив, что общим базовым типом обоих исключений является «Исключение», используйте его для переменной, содержащей оба исключения, а затем обрабатывайте только два захваченных исключения. Способ, которым это реализовано в IL, является «фильтром», который доступен в VB.Net.
Чтобы он работал в C #, нам нужна временная переменная с правильным базовым типом «Exception». Чтобы контролировать поток кода, мы можем добавить несколько веток. Поехали:
Очевидными недостатками этого являются то, что мы не можем правильно перебросить, и, давайте будем честными, это довольно уродливое решение. Уродливость можно немного исправить, выполнив удаление ветвей, что делает решение немного лучше:
Это оставляет только «перебросить». Чтобы это работало, нам нужно иметь возможность выполнять обработку внутри блока «catch» - и единственный способ выполнить эту работу - перехватывать объект «Exception».
На этом этапе мы можем добавить отдельную функцию, которая обрабатывает различные типы исключений с использованием разрешения перегрузки или для обработки исключений. Оба имеют недостатки. Для начала вот способ сделать это с помощью вспомогательной функции:
И другое решение - перехватить объект Exception и обработать его соответствующим образом. Наиболее буквальный перевод для этого, основанный на контексте выше, это:
Итак, сделаем вывод:
источник
Это классическая проблема, с которой сталкивается каждый разработчик C #.
Позвольте мне разбить ваш вопрос на 2 вопроса. Первый,
Могу ли я поймать несколько исключений одновременно?
Короче нет.
Что приводит к следующему вопросу,
Как избежать написания дублирующего кода, если я не могу перехватить несколько типов исключений в одном блоке catch ()?
Учитывая ваш конкретный пример, где резервное значение дешево построить, я бы хотел выполнить следующие шаги:
Итак, код выглядит так:
Если выдается какое-либо исключение, то WebId никогда не устанавливается на половину созданного значения и остается Guid.Empty.
Если создание запасного значения стоит дорого, а сброс значения намного дешевле, я бы переместил код сброса в его собственную функцию:
источник
То есть вы повторяете много кода в каждом переключателе исключений? Похоже, что извлечение метода было бы идеей бога, не так ли?
Итак, ваш код сводится к этому:
Интересно, почему никто не заметил это дублирование кода.
С C # 6 у вас также есть фильтры исключений, как уже упоминалось другими. Таким образом, вы можете изменить код выше к этому:
источник
Хотел добавить мой короткий ответ в эту уже длинную ветку. Кое-что, что не было упомянуто, является порядком приоритета операторов catch, более конкретно, вам необходимо знать область действия каждого типа исключения, которое вы пытаетесь перехватить.
Например, если вы используете исключение «catch-all» в качестве Exception, оно будет предшествовать всем остальным операторам catch, и вы, очевидно, получите ошибки компилятора, однако, если вы поменяете порядок, вы можете связать свои выражения catch (немного анти-паттерна, я думаю, ) вы можете поместить тип исключения «ловить все » внизу, и он будет фиксировать любые исключения, которые не учитывались выше в вашем блоке try..catch:
Я настоятельно рекомендую людям просмотреть этот документ MSDN:
Иерархия исключений
источник
Может быть, постараться сохранить свой код простым, например, помещая общий код в метод, как вы делали бы в любой другой части кода, не входящей в предложение catch?
Например:
Просто, как бы я это сделал, пытаясь найти простой красивый шаблон
источник
Обратите внимание, что я нашел один способ сделать это, но это больше похоже на материал для The Daily WTF :
источник
Здесь стоит упомянуть. Вы можете ответить на несколько комбинаций (Exception error и exception.message).
Я столкнулся с сценарием сценария использования при попытке привести управляющий объект в сетку данных с таким содержимым, как TextBox, TextBlock или CheckBox. В этом случае возвращенное исключение было таким же, но сообщение изменилось.
источник
Я хочу предложить кратчайший ответ (еще один функциональный стиль ):
Для этого вам нужно создать несколько перегрузок метода «Catch», аналогично System.Action:
и так далее сколько угодно. Но вам нужно сделать это один раз, и вы можете использовать его во всех своих проектах (или, если вы создали пакет nuget, мы могли бы использовать его тоже).
И реализация CatchMany:
PS Я не поставил нулевые проверки для простоты кода, рассмотрите возможность добавления проверки параметров.
ps2 Если вы хотите вернуть значение из catch, необходимо сделать те же методы Catch, но с параметрами Return и Func вместо Action.
источник
Просто позвоните попробуйте и поймать дважды.
Это так просто!
источник
В c # 6.0 Фильтры исключений - улучшения для обработки исключений
источник
catch (HttpException e) when e.GetHttpCode() == 400 { WriteLine("Bad Request"; }