Поймать несколько исключений одновременно?

Не рекомендуется просто ловить System.Exception. Вместо этого должны быть обнаружены только «известные» исключения.

Теперь это иногда приводит к ненужному повторяющемуся коду, например:

try
{
    WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
    WebId = Guid.Empty;
}
catch (OverflowException)
{
    WebId = Guid.Empty;
}

Интересно: есть ли способ перехватить оба исключения и позвонить только WebId = Guid.Emptyодин раз?

Данный пример довольно прост, так как это всего лишь GUID. Но представьте себе код, в котором вы несколько раз модифицируете объект, и если одна из манипуляций не удалась ожидаемым образом, вы хотите «сбросить» object. Однако, если есть неожиданное исключение, я все еще хочу бросить это выше.

25.09.2008 20:56:08
Если вы используете .net 4 и выше, я предпочитаю использовать aggregateexception msdn.microsoft.com/en-us/library/system.aggregateexception.aspx
Bepenfriends 18.10.2013 03:21:37
Bepenfriends - поскольку System.Guid не генерирует AggregateException , было бы здорово, если бы вы (или кто-то другой ) могли опубликовать ответ, показывающий, как вы бы включили его в AggregateException и т. Д.
weir 30.01.2014 18:53:54
DavidRR 14.08.2014 17:25:01
«Не рекомендуется просто ловить System.Exception». -и если метод может генерировать 32 типа исключений, что делает? написать улов для каждого из них отдельно?
giorgim 23.05.2015 20:22:20
Если метод выбрасывает 32 различных типа исключений, это плохо написано. Либо он не перехватывает исключения, которые делают его собственные вызовы, либо слишком много выполняет FAR в одном методе, либо большинство / все эти 32 должны быть одним исключением с кодом причины.
Flynn1179 19.05.2017 09:02:21
28 ОТВЕТОВ
РЕШЕНИЕ

Поймай System.Exceptionи включи типы

catch (Exception ex)            
{                
    if (ex is FormatException || ex is OverflowException)
    {
        WebId = Guid.Empty;
        return;
    }

    throw;
}
2078
26.11.2016 01:53:35
К сожалению, FxCop (т.е. Visual Studio Code Analysis) не нравится, когда вы ловите Exception.
Andrew Garrison 27.08.2010 19:48:16
Я согласен с тем, что не поймать Exception, но в этом случае улов - это фильтр. Вы можете иметь уровень выше, который будет обрабатывать другие типы исключений. Я бы сказал, что это правильно, хотя он включает в себя улов (исключение х). Он не изменяет поток программы, он просто обрабатывает определенные исключения, а затем позволяет остальной части приложения работать с любыми другими типами исключений.
lkg 14.06.2011 19:13:51
Последняя версия FxCop не выдает исключение при использовании кода выше.
Peter 8.07.2011 05:12:06
Во-первых, не уверен, что не так с кодом ОП. Ответ № 1 принят почти вдвое больше строк и гораздо менее читабелен.
João Bragança 4.09.2012 21:57:49
@ JoãoBragança: Хотя в этом ответе в этом примере используется больше строк, попробуйте представить, например, имеете ли вы дело с файловым вводом-выводом, и все, что вы хотите сделать, это перехватить эти исключения и сделать некоторые сообщения в журнале, но только те, которые вы ожидаете получить от своего методы файлового ввода-вывода. Тогда вам часто приходится иметь дело с большим количеством (около 5 или более) различных типов исключений. В такой ситуации такой подход может сэкономить вам несколько строк.
Xilconic 28.11.2013 14:47:07

Обратите внимание, что я нашел один способ сделать это, но это больше похоже на материал для The Daily WTF :

catch (Exception ex)
{
    switch (ex.GetType().Name)
    {
        case "System.FormatException":
        case "System.OverflowException":
            WebId = Guid.Empty;
            break;
        default:
            throw;
    }
}
3
9.04.2015 14:07:34
-1 голос, +5 WTF :-) Это не должно было быть помечено как ответ, но он щедрый.
Aaron 25.09.2008 21:23:19
Неважно, насколько просто мы могли бы это сделать. Но он не сидел без дела и высказал свое мнение, чтобы решить это. Очень ценю.
Maxymus 21.01.2016 10:27:36
Хотя на самом деле не делайте этого, используйте Фильтры исключений в C # 6 или любой другой ответ - я специально обозначил это как «Это один из способов, но это плохо, и я хочу сделать что-то лучше».
Michael Stum♦ 21.01.2016 19:16:45
ПОЧЕМУ это плохо? Я был озадачен тем, что вы не можете напрямую использовать исключение в операторе switch.
MKesper 8.06.2016 14:02:18
@MKesper Я вижу несколько причин, по которым это плохо. Это требует написания полных имен классов в виде строковых литералов, что уязвимо для опечаток, от которых компилятор не сможет вас спасти. (Это важно, поскольку во многих магазинах случаи ошибок менее проверены, и поэтому тривиальные ошибки в них, скорее всего, будут упущены.) Также не удастся найти исключение, которое является подклассом одного из указанных случаев. И из-за того, что они являются строками, случаи будут пропущены такими инструментами, как VS «Найти все ссылки» - это уместно, если вы хотите добавить шаг очистки везде, где обнаруживается конкретное исключение.
Mark Amery 23.10.2017 22:18:03

Как насчет

try
{
    WebId = Guid.Empty;
    WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
}
catch (OverflowException)
{
}
13
22.03.2013 15:58:32
Это работает, только если Catch-код может быть полностью перемещен в Try-Block. Но код обработки изображений, когда вы выполняете несколько манипуляций с объектом, а одна посередине, терпит неудачу, и вы хотите «сбросить» объект.
Michael Stum♦ 25.09.2008 20:59:36
В этом случае я бы добавил функцию сброса и вызвал бы ее из нескольких блоков catch.
Maurice 25.09.2008 21:04:02

@Micheal

Немного переработанная версия вашего кода:

catch (Exception ex)
{
   Type exType = ex.GetType();
   if (exType == typeof(System.FormatException) || 
       exType == typeof(System.OverflowException)
   {
       WebId = Guid.Empty;
   } else {
      throw;
   }
}

Сравнение строк уродливо и медленно.

16
25.09.2008 21:16:23
Почему бы просто не использовать ключевое слово "is"?
Chris Pietschmann 25.09.2008 21:02:47
@Michael - Если Microsoft представила, скажем, StringTooLongException, полученную из FormatException, то это все еще исключение формата, просто конкретное. Это зависит от того, хотите ли вы семантику «поймать этот точный тип исключения» или «поймать исключения, которые означают, что формат строки был неправильным».
Greg Beech 25.09.2008 21:19:31
@Michael - Также обратите внимание, что «catch (FormatException ex) имеет последнюю семантику, она будет отлавливать все, что происходит от FormatException.
Greg Beech 25.09.2008 21:21:23
@Alex No. «throw» без «ex» содержит исходное исключение, включая исходную трассировку стека. Добавление «ex» приводит к сбросу трассировки стека, так что вы получите исключение, отличное от оригинала. Я уверен, что кто-то другой может объяснить это лучше, чем я. :)
Samantha Branham 22.09.2010 22:04:22
-1: Этот код является чрезвычайно хрупким - библиотека разработчик может ожидать , чтобы заменить throw new FormatException();с , throw new NewlyDerivedFromFormatException();не нарушая код , используя библиотеку, и это будет справедливо для всех обработки исключений , кроме случаев , когда кто - то использовал ==вместо is(или просто catch (FormatException)).
Sam Harwell 17.07.2013 03:47:34

К сожалению, не в C #, поскольку для этого вам понадобится фильтр исключений, а C # не предоставляет эту возможность MSIL. VB.NET имеет такую ​​возможность, например,

Catch ex As Exception When TypeOf ex Is FormatException OrElse TypeOf ex Is OverflowException

Что вы можете сделать, это использовать анонимную функцию для инкапсуляции вашего кода ошибки, а затем вызывать его в этих определенных блоках перехвата:

Action onError = () => WebId = Guid.Empty;
try
{
    // something
}
catch (FormatException)
{
    onError();
}
catch (OverflowException)
{
    onError();
}
188
25.09.2008 21:03:57
Интересная идея и еще один пример того, что VB.net иногда имеет некоторые интересные преимущества перед C #
Michael Stum♦ 25.09.2008 21:19:01
@MichaelStum с этого вида синтаксиса я бы трудно назвать интересным вообще ... содроганием
MarioDS 9.10.2014 12:15:06
Фильтры исключений появятся в c # 6! Обратите внимание на разницу в использовании фильтров в пользу повторного бросания roslyn.codeplex.com/discussions/541301
Arne Deruwe 26.11.2014 13:26:22
@ArneDeruwe Спасибо за эту ссылку! Я только что узнал еще одну важную причину, чтобы не перебрасывать: throw e;уничтожает stacktrace и callstack, throw;уничтожает "only" callstack (делая ненужные аварийные дампы!). Очень хорошая причина, чтобы использовать ни то, ни другое, если этого можно избежать!
AnorZaken 11.12.2014 14:53:36
Если я не ошибаюсь, вы также можете пропустить ловушку в VB, как вы можете с помощью операторов case или (переключить в c #), например так. Try Catch ex As ArgumentException Catch ex As NullReferenceException End TryНо, к сожалению, C # не делает так, что у нас остался вспомогательный метод или для общего определения и определения типа ,
David Carrigan 17.09.2015 16:13:56
catch (Exception ex)
{
    if (!(
        ex is FormatException ||
        ex is OverflowException))
    {
        throw;
    }
    Console.WriteLine("Hello");
}
12
30.10.2014 11:15:16

Принятый ответ кажется приемлемым, за исключением того, что CodeAnalysis / FxCop будет жаловаться на тот факт, что он перехватывает общий тип исключения.

Кроме того, кажется, что оператор is может слегка ухудшить производительность.

CA1800: Не приводите без необходимости излишние выражения «рассмотрите возможность проверки результата оператора« as »вместо этого», но если вы сделаете это, вы будете писать больше кода, чем если бы вы ловили каждое исключение отдельно.

Во всяком случае, вот что я бы сделал:

bool exThrown = false;

try
{
    // Something
}
catch (FormatException) {
    exThrown = true;
}
catch (OverflowException) {
    exThrown = true;
}

if (exThrown)
{
    // Something else
}
20
9.04.2015 14:09:55
Но имейте в виду, что вы не можете сбросить исключение, не потеряв трассировку стека, если вы сделаете это так. (См. Комментарий Майкла Стума к принятому ответу)
René 9.12.2010 11:27:20
Этот шаблон можно улучшить, сохранив исключение (пожалуйста, извините за плохое форматирование - я не могу понять, как поместить код в комментарии): Exception ex = null; try {// что-то} catch (FormatException e) {ex = e; } catch (OverflowException e) {ex = e; } if (ex! = null) {// что-то еще и иметь дело с бывшим}
Jesse Weigert 16.03.2012 20:03:35
@JesseWeigert: 1. Вы можете использовать обратные пометки, чтобы придать фрагменту текста моноширинный шрифт и светло-серый фон. 2. Вы все еще не сможете сбросить исходное исключение, включая трассировку стека .
Oliver 22.11.2012 17:19:07
@CleverNeologism, хотя это может быть правдой, что использование isоператора может оказать небольшое негативное влияние на производительность, также верно, что обработчик исключений не место, чтобы чрезмерно беспокоиться об оптимизации производительности. Если ваше приложение тратит так много времени на обработчики исключений, что оптимизация производительности может реально повлиять на производительность приложения, то есть другие проблемы с кодом, на которые следует обратить пристальное внимание. Сказав это, мне все еще не нравится это решение, потому что вы теряете трассировку стека и потому что очистка контекстуально удаляется из оператора catch.
Craig 15.09.2015 07:22:11
Единственный раз, когда isоператор снижает производительность, это если вы позже выполняете asоперацию (следовательно, они необоснованно квалифицируют правило ). Если все, что вы делаете, - это тестирование приведения без необходимости его выполнения, то isоператор - именно то, что вы хотите использовать.
saluce 13.11.2015 16:25:20

Это вариант ответа Мэтта (я чувствую, что это немного чище) ... используйте метод:

public void TryCatch(...)
{
    try
    {
       // something
       return;
    }
    catch (FormatException) {}
    catch (OverflowException) {}

    WebId = Guid.Empty;
}

Любые другие исключения будут выброшены, и код WebId = Guid.Empty;не будет поражен. Если вы не хотите, чтобы другие исключения вызывали сбой вашей программы, просто добавьте это ПОСЛЕ двух других зацепок:

...
catch (Exception)
{
     // something, if anything
     return; // only need this if you follow the example I gave and put it all in a method
}
18
11.10.2013 20:52:50
-1 Это будет выполнено WebId = Guid.Emtpyв случае, если не было сгенерировано исключение.
Sepster 23.10.2012 13:41:38
@sepster Я думаю, что выражение возврата после "// что-то" подразумевается здесь. Мне не очень нравится решение, но это конструктивный вариант в обсуждении. +1, чтобы отменить ваше downvote :-)
toong 23.10.2012 21:12:57
@Sepster toong прав, я предполагал, что если вы хотите получить возврат, вы бы поставили один ... Я пытался сделать свой ответ достаточно общим, чтобы его можно было применить ко всем ситуациям на случай, если другие с похожими, но не точными вопросами выиграют как хорошо. Однако, для хорошей меры, я добавил returnк своему ответу. Спасибо за вклад.
bsara 24.10.2012 17:27:37

Для полноты, начиная с .NET 4.0 код можно переписать так:

Guid.TryParse(queryString["web"], out WebId);

TryParse никогда не генерирует исключения и возвращает false, если формат неверный, устанавливая WebId в Guid.Empty.


Начиная с C # 7, вы можете избежать введения переменной в отдельной строке:

Guid.TryParse(queryString["web"], out Guid webId);

Вы также можете создать методы для анализа возвращаемых кортежей, которые еще не доступны в .NET Framework начиная с версии 4.6:

(bool success, Guid result) TryParseGuid(string input) =>
    (Guid.TryParse(input, out Guid result), result);

И используйте их так:

WebId = TryParseGuid(queryString["web"]).result;
// or
var tuple = TryParseGuid(queryString["web"]);
WebId = tuple.success ? tuple.result : DefaultWebId;

Следующее бесполезное обновление этого бесполезного ответа происходит, когда деконструкция out-параметров реализована в C # 12. :)

134
18.03.2017 12:40:01
Точно - кратко, и вы полностью обходите снижение производительности обработки исключений, плохую форму преднамеренного использования исключений для управления потоком программ и мягкую направленность распространения вашей логики преобразования, немного здесь и немного там ,
Craig 17.04.2013 03:56:53
Я знаю, что вы имели в виду, но, конечно, Guid.TryParseникогда не вернется Guid.Empty. Если строка имеет неправильный формат, она устанавливает resultвыходной параметр в значение Guid.Empty, но возвращает false . Я упоминаю об этом, потому что я видел код, который делает вещи в стиле Guid.TryParse(s, out guid); if (guid == Guid.Empty) { /* handle invalid s */ }, который обычно неверен, если sможет быть строковым представлением Guid.Empty.
user743382 18.05.2013 11:39:00
вау, ты ответил на вопрос, за исключением того, что это не в духе вопроса. Большая проблема в другом :(
nawfal 18.05.2013 20:01:31
Правильный шаблон для использования TryParse, конечно же, больше похож на тот if( Guid.TryParse(s, out guid){ /* success! */ } else { /* handle invalid s */ }, который не оставляет двусмысленности, как сломанный пример, где входное значение может фактически быть строковым представлением Guid.
Craig 22.02.2014 01:55:47
Этот ответ действительно может быть правильным в отношении Guid.Parse, но он упустил весь смысл первоначального вопроса. Который не имеет ничего общего с Guid.Parse, но был связан с перехватом Exception vs FormatException / OverflowException / и т.д.
Conor Gallagher 14.09.2015 12:39:11

Предостережение и предупреждение: еще один вид, функциональный стиль.

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

static void Main() 
{ 
    Action body = () => { ...your code... };

    body.Catch<InvalidOperationException>() 
        .Catch<BadCodeException>() 
        .Catch<AnotherException>(ex => { ...handler... })(); 
}

(В основном, обеспечивает другую пустую Catchперегрузку, которая возвращает себя)

Большой вопрос к этому - почему . Я не думаю, что стоимость перевешивает выигрыш здесь :)

11
18.05.2013 11:28:30
Одним из возможных преимуществ этого подхода является то, что существует семантическая разница между перехватом и повторным выбросом исключения по сравнению с не перехватом его; в некоторых случаях код должен действовать на исключение, не перехватывая его. Такое возможно в vb.net, но не в C #, если только не используется оболочка, написанная на vb.net и вызываемая из C #.
supercat 17.07.2013 02:43:23
Как действует исключение, не уловив его? Я не до конца понимаю вас.
nawfal 17.07.2013 07:20:45
@nawful ... с использованием фильтра vb - функция фильт (например, исключение): LogEx (ex): вернуть false ... затем в строке «поймать»: поймать ex, когда фильтр (ex)
FastAl 7.10.2015 18:15:59
@FastAl Разве это не то, что фильтры исключений позволяют в C # 6?
HimBromBeere 22.09.2016 11:52:54
@HimBromBeere, да, они прямые аналоги
FastAl 22.09.2016 14:31:44

РЕДАКТИРОВАТЬ: Я согласен с другими, которые говорят, что, начиная с C # 6.0, фильтры исключений теперь являются идеальным способом:catch (Exception ex) when (ex is ... || ex is ... )

За исключением того, что я все еще ненавижу макет с одной строкой и лично выложу код, как показано ниже. Я думаю, что это так же функционально, как и эстетично, так как я считаю, что это улучшает понимание. Некоторые могут не согласиться:

catch (Exception ex) when (
    ex is ...
    || ex is ...
    || ex is ...
)

ОРИГИНАЛ:

Я знаю, что немного опоздал на вечеринку здесь, но святой дым ...

Переходя к погоне, этот вид дублирует более ранний ответ, но если вы действительно хотите выполнить общее действие для нескольких типов исключений и сохранить все в чистоте и порядке в рамках одного метода, почему бы просто не использовать лямбду / Закрытие / встроенная функция, чтобы сделать что-то вроде следующего? Я имею в виду, что вполне вероятно, что в итоге вы поймете, что просто хотите сделать это закрытие отдельным методом, который вы можете использовать повсюду. Но тогда это будет очень легко сделать без реального структурного изменения остальной части кода. Правильно?

private void TestMethod ()
{
    Action<Exception> errorHandler = ( ex ) => {
        // write to a log, whatever...
    };

    try
    {
        // try some stuff
    }
    catch ( FormatException  ex ) { errorHandler ( ex ); }
    catch ( OverflowException ex ) { errorHandler ( ex ); }
    catch ( ArgumentNullException ex ) { errorHandler ( ex ); }
}

Я не могу не задаться вопросом ( предупреждение: впереди немножко иронии / сарказма), зачем вообще все эти усилия просто заменять следующим:

try
{
    // try some stuff
}
catch( FormatException ex ){}
catch( OverflowException ex ){}
catch( ArgumentNullException ex ){}

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

// sorta sucks, let's be honest...
try
{
    // try some stuff
}
catch( Exception ex )
{
    if (ex is FormatException ||
        ex is OverflowException ||
        ex is ArgumentNullException)
    {
        // write to a log, whatever...
        return;
    }
    throw;
}

Потому что это, конечно, не более автоматически читается.

Конечно, я оставил три идентичных экземпляра /* write to a log, whatever... */ return;из первого примера.

Но это своего рода моя точка зрения. Вы все слышали о функциях / методах, верно? Шутки в сторону. Напишите общую ErrorHandlerфункцию и, например, вызывайте ее из каждого блока catch.

Если вы спросите меня, второй пример (с ifи isключевыми словами) одновременно значительно менее читаемый, и одновременно значительно более подвержены ошибкам во время фазы вашего проекта технического обслуживания.

Этап обслуживания для тех, кто может быть относительно новичком в программировании, будет составлять 98,7% или более от общего срока службы вашего проекта, и бедняга, выполняющий обслуживание, почти наверняка будет кем-то другим, кроме вас. И есть очень хороший шанс, что они будут тратить 50% своего времени на работу, ругаясь на ваше имя.

И, конечно же FxCop лает на вас , и поэтому вы должны также добавить атрибут в свой код , который точно пронестись делать с запущенной программой, и только там , чтобы сказать FxCop игнорировать вопрос , что в 99,9% случаев он полностью правильно в маркировке. И, извините, я могу ошибаться, но разве этот атрибут «игнорировать» не скомпилирован в ваше приложение?

Поможет ли размещение всего ifтеста в одной строке сделать его более читабельным? Я так не думаю. Я имею в виду, у меня когда-то был другой программист, яростно споривший однажды, что размещение большего количества кода в одной строке заставит его «работать быстрее». Но, конечно, он был совершенно безумным. Попытка объяснить ему (с открытым лицом - что было непросто), как интерпретатор или компилятор разбил бы эту длинную строку на отдельные операторы с одной инструкцией на строку - по существу, идентичные результату, если бы он пошел вперед и просто сделал код читабельным вместо того, чтобы пытаться перехитрить компилятор - не оказал на него никакого влияния. Но я отвлекся.

Насколько менее читабельным это становится, если добавить еще три типа исключений через месяц или два? (Ответ: это становится намного менее читабельным).

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

Просто говорю...

// super sucks...
catch( Exception ex )
{
    if ( ex is FormatException || ex is OverflowException || ex is ArgumentNullException )
    {
        // write to a log, whatever...
        return;
    }
    throw;
}
577
14.02.2017 20:38:54
Когда я впервые наткнулся на этот вопрос, я был во всем принятом ответе. Круто, я могу просто поймать все Exceptions и проверить тип. Я думал, что это очистило код, но что-то заставляло меня возвращаться к вопросу, и я фактически читал другие ответы на вопрос. Я жевал это некоторое время, но я должен согласиться с вами. Это более читаемым и ремонтопригодны использовать функцию сушки ваш код , чем поймать все, проверьте тип сравнения со списком, код оберточной и метания. Спасибо за опоздание и предоставление альтернативного и вменяемого (IMO) варианта. +1.
erroric 6.03.2014 14:55:27
Использование функции обработки ошибок не сработает, если вы захотите включить throw;. Вам придется повторять эту строку кода в каждом блоке catch (очевидно, это не конец света, но стоит упомянуть, поскольку это код, который нужно будет повторять).
kad81 17.06.2014 08:48:33
@ kad81, это правда, но вы все равно получите преимущество написания кода регистрации и очистки в одном месте и его изменения в одном месте, если это необходимо, без глупой семантики перехвата базового типа исключения, а затем ветвления на основе тип исключения. И то, что один дополнительный throw();оператор в каждом блоке улова - это небольшая цена, IMO, и все равно оставляет вам возможность выполнить дополнительную очистку для конкретного типа исключения, если это необходимо.
Craig 18.06.2014 18:39:36
Привет @ Reitffunk, просто используйте Func<Exception, MyEnumType>вместо Action<Exception>. То есть Func<T, Result>с Resultтипом возврата.
Craig 30.03.2015 16:42:12
Я полностью согласен здесь. Я тоже прочитал первый ответ, и мысль кажется логичной. Перемещен в общий 1 для всех обработчиков исключений. Что-то внутри меня заставило меня внутренне тошнить ... так что я перевернул код. Потом наткнулся на эту красавицу! Это должен быть принятый ответ
Conor Gallagher 14.09.2015 12:33:04

Обновление 2015-12-15: см. Https://stackoverflow.com/a/22864936/1718702 для C # 6. Это чище и теперь стандарт в языке.

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

У меня уже было это расширение в моей библиотеке, изначально написанное для других целей, но оно отлично работало для typeпроверки исключений. Плюс, имхо, это выглядит чище, чем куча ||утверждений. Кроме того, в отличие от принятого ответа, я предпочитаю явную обработку исключений, поэтому ex is ...имел нежелательное поведение, так как классы наследования могут быть назначены родительским типам).

использование

if (ex.GetType().IsAnyOf(
    typeof(FormatException),
    typeof(ArgumentException)))
{
    // Handle
}
else
    throw;

Расширение IsAnyOf.cs (см. Пример полной обработки ошибок для зависимостей)

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter matches at least one of the passed in comparisons.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_comparisons">Values to compare against.</param>
        /// <returns>True if a match is found.</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static bool IsAnyOf<T>(this T p_parameter, params T[] p_comparisons)
        {
            // Validate
            p_parameter
                .CannotBeNull("p_parameter");
            p_comparisons
                .CannotBeNullOrEmpty("p_comparisons");

            // Test for any match
            foreach (var item in p_comparisons)
                if (p_parameter.Equals(item))
                    return true;

            // Return no matches found
            return false;
        }
    }
}

Пример полной обработки ошибок (копирование-вставка в новое консольное приложение)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common.FluentValidation;

namespace IsAnyOfExceptionHandlerSample
{
    class Program
    {
        static void Main(string[] args)
        {
            // High Level Error Handler (Log and Crash App)
            try
            {
                Foo();
            }
            catch (OutOfMemoryException ex)
            {
                Console.WriteLine("FATAL ERROR! System Crashing. " + ex.Message);
                Console.ReadKey();
            }
        }

        static void Foo()
        {
            // Init
            List<Action<string>> TestActions = new List<Action<string>>()
            {
                (key) => { throw new FormatException(); },
                (key) => { throw new ArgumentException(); },
                (key) => { throw new KeyNotFoundException();},
                (key) => { throw new OutOfMemoryException(); },
            };

            // Run
            foreach (var FooAction in TestActions)
            {
                // Mid-Level Error Handler (Appends Data for Log)
                try
                {
                    // Init
                    var SomeKeyPassedToFoo = "FooParam";

                    // Low-Level Handler (Handle/Log and Keep going)
                    try
                    {
                        FooAction(SomeKeyPassedToFoo);
                    }
                    catch (Exception ex)
                    {
                        if (ex.GetType().IsAnyOf(
                            typeof(FormatException),
                            typeof(ArgumentException)))
                        {
                            // Handle
                            Console.WriteLine("ex was {0}", ex.GetType().Name);
                            Console.ReadKey();
                        }
                        else
                        {
                            // Add some Debug info
                            ex.Data.Add("SomeKeyPassedToFoo", SomeKeyPassedToFoo.ToString());
                            throw;
                        }
                    }
                }
                catch (KeyNotFoundException ex)
                {
                    // Handle differently
                    Console.WriteLine(ex.Message);

                    int Count = 0;
                    if (!Validate.IsAnyNull(ex, ex.Data, ex.Data.Keys))
                        foreach (var Key in ex.Data.Keys)
                            Console.WriteLine(
                                "[{0}][\"{1}\" = {2}]",
                                Count, Key, ex.Data[Key]);

                    Console.ReadKey();
                }
            }
        }
    }
}

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter matches at least one of the passed in comparisons.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_comparisons">Values to compare against.</param>
        /// <returns>True if a match is found.</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static bool IsAnyOf<T>(this T p_parameter, params T[] p_comparisons)
        {
            // Validate
            p_parameter
                .CannotBeNull("p_parameter");
            p_comparisons
                .CannotBeNullOrEmpty("p_comparisons");

            // Test for any match
            foreach (var item in p_comparisons)
                if (p_parameter.Equals(item))
                    return true;

            // Return no matches found
            return false;
        }

        /// <summary>
        /// Validates if any passed in parameter is equal to null.
        /// </summary>
        /// <param name="p_parameters">Parameters to test for Null.</param>
        /// <returns>True if one or more parameters are null.</returns>
        public static bool IsAnyNull(params object[] p_parameters)
        {
            p_parameters
                .CannotBeNullOrEmpty("p_parameters");

            foreach (var item in p_parameters)
                if (item == null)
                    return true;

            return false;
        }
    }
}

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter is not null, throwing a detailed exception message if the test fails.
        /// </summary>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_name">Name of tested parameter to assist with debugging.</param>
        /// <exception cref="ArgumentNullException"></exception>
        public static void CannotBeNull(this object p_parameter, string p_name)
        {
            if (p_parameter == null)
                throw
                    new
                        ArgumentNullException(
                        string.Format("Parameter \"{0}\" cannot be null.",
                        p_name), default(Exception));
        }
    }
}

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter is not null or an empty collection, throwing a detailed exception message if the test fails.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_name">Name of tested parameter to assist with debugging.</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public static void CannotBeNullOrEmpty<T>(this ICollection<T> p_parameter, string p_name)
        {
            if (p_parameter == null)
                throw new ArgumentNullException("Collection cannot be null.\r\nParameter_Name: " + p_name, default(Exception));

            if (p_parameter.Count <= 0)
                throw new ArgumentOutOfRangeException("Collection cannot be empty.\r\nParameter_Name: " + p_name, default(Exception));
        }

        /// <summary>
        /// Validates the passed in parameter is not null or empty, throwing a detailed exception message if the test fails.
        /// </summary>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_name">Name of tested parameter to assist with debugging.</param>
        /// <exception cref="ArgumentException"></exception>
        public static void CannotBeNullOrEmpty(this string p_parameter, string p_name)
        {
            if (string.IsNullOrEmpty(p_parameter))
                throw new ArgumentException("String cannot be null or empty.\r\nParameter_Name: " + p_name, default(Exception));
        }
    }
}

Два примера модульных тестов NUnit

Поведение соответствия для Exceptionтипов является точным (т. Е. Дочерний элемент НЕ соответствует ни одному из его родительских типов).

using System;
using System.Collections.Generic;
using Common.FluentValidation;
using NUnit.Framework;

namespace UnitTests.Common.Fluent_Validations
{
    [TestFixture]
    public class IsAnyOf_Tests
    {
        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void IsAnyOf_ArgumentNullException_ShouldNotMatch_ArgumentException_Test()
        {
            Action TestMethod = () => { throw new ArgumentNullException(); };

            try
            {
                TestMethod();
            }
            catch (Exception ex)
            {
                if (ex.GetType().IsAnyOf(
                    typeof(ArgumentException), /*Note: ArgumentNullException derrived from ArgumentException*/
                    typeof(FormatException),
                    typeof(KeyNotFoundException)))
                {
                    // Handle expected Exceptions
                    return;
                }

                //else throw original
                throw;
            }
        }

        [Test, ExpectedException(typeof(OutOfMemoryException))]
        public void IsAnyOf_OutOfMemoryException_ShouldMatch_OutOfMemoryException_Test()
        {
            Action TestMethod = () => { throw new OutOfMemoryException(); };

            try
            {
                TestMethod();
            }
            catch (Exception ex)
            {
                if (ex.GetType().IsAnyOf(
                    typeof(OutOfMemoryException),
                    typeof(StackOverflowException)))
                    throw;

                /*else... Handle other exception types, typically by logging to file*/
            }
        }
    }
}
9
23.05.2017 10:31:35
Улучшение языка не является «более элегантным». Во многих местах это фактически создало адский уровень обслуживания. Спустя годы многие программисты не гордятся тем, какого монстра они создали. Это не то, что вы привыкли читать. Это может вызвать "да?" эффект или даже серьезные "WTFs". Это иногда сбивает с толку. Единственное, что он делает - это усложняет понимание кода для тех, кому нужно иметь дело с ним позже в процессе обслуживания - только потому, что один программист пытался быть «умным». За эти годы я узнал, что эти «умные» решения редко бывают хорошими.
Kaii 9.10.2014 21:02:42
или в нескольких словах: придерживайтесь возможностей, которые предоставляет родной язык. не пытайтесь переопределить семантику языка только потому, что они вам не нравятся. Ваши коллеги (и, возможно, будущие я) будут вам благодарны, если честно.
Kaii 9.10.2014 21:07:15
Также обратите внимание, что ваше решение только приближает семантику C # 6 when, как и любая версия catch (Exception ex) {if (...) {/*handle*/} throw;}. Реальное значение whenзаключается в том, что фильтр запускается до того, как исключение будет перехвачено , что позволяет избежать повреждения затрат / стека при повторном выбросе. Он использует функцию CLR, которая ранее была доступна только для VB и MSIL.
Marc L. 27.06.2016 14:55:35
Более элегантно? Этот пример настолько велик для такой простой задачи, а код выглядит так ужасно, что даже не стоило на него смотреть. Пожалуйста, не делайте этот код чужой проблемой в реальном проекте.
KthProg 28.12.2016 16:02:47
Весь ваш IsAnyOfметод можно переписать так же простоp_comparisons.Contains(p_parameter)
maksymiuk 27.07.2017 16:04:25

Как уже отмечали другие, вы можете иметь ifоператор внутри блока catch, чтобы определить, что происходит. C # 6 поддерживает фильтры исключений, поэтому будет работать следующее:

try {  }
catch (Exception e) when (MyFilter(e))
{
    
}

Затем MyFilterметод может выглядеть примерно так:

private bool MyFilter(Exception e)
{
  return e is ArgumentNullException || e is FormatException;
}

В качестве альтернативы, все это можно сделать встроенным (правая часть оператора when просто должна быть логическим выражением).

try {  }
catch (Exception e) when (e is ArgumentNullException || e is FormatException)
{
    
}

Это отличается от использования ifоператора внутри catchблока, использование фильтров исключений не размотает стек.

Вы можете скачать Visual Studio 2015, чтобы проверить это.

Если вы хотите продолжить использовать Visual Studio 2013, вы можете установить следующий пакет nuget:

Установить-пакет Microsoft.Net.Compilers

На момент написания, это будет включать поддержку C # 6.

Ссылка на этот пакет приведет к тому, что проект будет построен с использованием конкретной версии компиляторов C # и Visual Basic, содержащихся в пакете, в отличие от любой установленной версии системы.

364
6.10.2015 18:11:12
Терпеливо жду официального релиза 6 ... Мне бы хотелось, чтобы это получило чек, когда это произойдет.
RubberDuck 14.02.2015 13:41:32
@RubberDuck Я умираю за нулевой оператор распространения из C # 6. Пытаюсь убедить остальную часть моей команды, что риск нестабильного языка / компилятора того стоит. Множество мелких улучшений с огромным влиянием. Что касается того, чтобы быть отмеченным как ответ, не важно, пока люди понимают, что это будет / возможно, я счастлив.
Joe 14.02.2015 19:52:07
Правильно?! Я буду внимательно смотреть на мою базу кода в ближайшем будущем. =) Я знаю, что проверка не важна, но, учитывая, что принятый ответ скоро устареет, я надеюсь, что OP вернется, чтобы проверить это, чтобы дать ему надлежащую видимость.
RubberDuck 14.02.2015 20:02:24
Это частично, почему я еще не награждал это @Joe. Я хочу, чтобы это было видно. Вы можете добавить пример встроенного фильтра для ясности, хотя.
RubberDuck 6.10.2015 16:15:57

Поскольку я чувствовал, что эти ответы только коснулись поверхности, я попытался копнуть немного глубже.

Итак, что мы действительно хотим сделать, это то, что не компилируется, скажем:

// Won't compile... damn
public static void Main()
{
    try
    {
        throw new ArgumentOutOfRangeException();
    }
    catch (ArgumentOutOfRangeException)
    catch (IndexOutOfRangeException) 
    {
        // ... handle
    }

Причина, по которой мы этого хотим, заключается в том, что мы не хотим, чтобы обработчик исключений перехватывал то, что нам нужно позже в процессе. Конечно, мы можем поймать исключение и проверить, что делать, если «да», но давайте будем честными, мы не очень этого хотим. (FxCop, проблемы с отладчиком, уродство)

Так почему же этот код не скомпилируется - и как мы можем взломать его таким образом, чтобы он был?

Если мы посмотрим на код, то, что мы действительно хотели бы сделать, это переадресовать вызов. Однако, согласно MS Partition II, блоки обработчиков исключений IL не будут работать таким образом, что в этом случае имеет смысл, поскольку это подразумевает, что объект «исключения» может иметь разные типы.

Или, чтобы написать это в коде, мы просим компилятор сделать что-то вроде этого (ну, это не совсем правильно, но я думаю, это самая близкая вещь):

// Won't compile... damn
try
{
    throw new ArgumentOutOfRangeException();
}
catch (ArgumentOutOfRangeException e) {
    goto theOtherHandler;
}
catch (IndexOutOfRangeException e) {
theOtherHandler:
    Console.WriteLine("Handle!");
}

Причина, по которой это не скомпилируется, совершенно очевидна: какой тип и значение будет иметь объект «$ exception» (которые здесь хранятся в переменных «e»)? Мы хотим, чтобы компилятор справился с этим, отметив, что общим базовым типом обоих исключений является «Исключение», используйте его, чтобы переменная содержала оба исключения, а затем обрабатывайте только два перехваченных исключения. Способ, которым это реализовано в IL, является «фильтром», который доступен в VB.Net.

Чтобы он работал в C #, нам нужна временная переменная с правильным базовым типом «Exception». Чтобы контролировать поток кода, мы можем добавить несколько веток. Поехали:

    Exception ex;
    try
    {
        throw new ArgumentException(); // for demo purposes; won't be caught.
        goto noCatch;
    }
    catch (ArgumentOutOfRangeException e) {
        ex = e;
    }
    catch (IndexOutOfRangeException e) {
        ex = e;
    }

    Console.WriteLine("Handle the exception 'ex' here :-)");
    // throw ex ?

noCatch:
    Console.WriteLine("We're done with the exception handling.");

Очевидными недостатками этого являются то, что мы не можем правильно перебросить, и, давайте будем честными, это довольно уродливое решение. Уродство можно немного исправить, выполнив удаление ветвей, что делает решение немного лучше:

Exception ex = null;
try
{
    throw new ArgumentException();
}
catch (ArgumentOutOfRangeException e)
{
    ex = e;
}
catch (IndexOutOfRangeException e)
{
    ex = e;
}
if (ex != null)
{
    Console.WriteLine("Handle the exception here :-)");
}

Это оставляет только «перебросить». Чтобы это работало, нам нужно иметь возможность выполнять обработку внутри блока «catch» - и единственный способ выполнить эту работу - перехватывать объект «Exception».

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

private static bool Handle(Exception e)
{
    Console.WriteLine("Handle the exception here :-)");
    return true; // false will re-throw;
}

public static void Main()
{
    try
    {
        throw new OutOfMemoryException();
    }
    catch (ArgumentException e)
    {
        if (!Handle(e)) { throw; }
    }
    catch (IndexOutOfRangeException e)
    {
        if (!Handle(e)) { throw; }
    }

    Console.WriteLine("We're done with the exception handling.");

И другое решение - перехватить объект Exception и обработать его соответствующим образом. Наиболее буквальный перевод для этого, основанный на контексте выше, это:

try
{
    throw new ArgumentException();
}
catch (Exception e)
{
    Exception ex = (Exception)(e as ArgumentException) ?? (e as IndexOutOfRangeException);
    if (ex != null)
    {
        Console.WriteLine("Handle the exception here :-)");
        // throw ?
    }
    else 
    {
        throw;
    }
}

Итак, сделаем вывод:

  • Если мы не хотим перебрасывать, мы можем рассмотреть возможность перехвата правильных исключений и их временного хранения.
  • Если обработчик прост, и мы хотим повторно использовать код, лучшим решением, вероятно, будет введение вспомогательной функции.
  • Если мы хотим перезапустить, у нас нет другого выбора, кроме как поместить код в обработчик перехвата 'Exception', который сломает FxCop и неперехваченные исключения вашего отладчика.
7
21.10.2014 09:44:02

Ответ Джозефа Дейгла - хорошее решение, но я обнаружил, что следующая структура немного более аккуратна и менее подвержена ошибкам.

catch(Exception ex)
{   
    if (!(ex is SomeException || ex is OtherException)) throw;

    // Handle exception
}

Есть несколько преимуществ обращения выражения:

  • Заявление о возврате не требуется
  • Код не является вложенным
  • Нет риска забыть утверждения «throw» или «return», которые в решении Иосифа отделены от выражения.

Его можно даже сжать в одну строку (хотя и не очень красиво)

catch(Exception ex) { if (!(ex is SomeException || ex is OtherException)) throw;

    // Handle exception
}

Edit: фильтрация исключений в C # 6.0 будет сделать синтаксис немного чище и поставляется с рядом других преимуществ над любым текущим решением. (прежде всего оставляя стек без повреждений)

Вот как будет выглядеть та же проблема с использованием синтаксиса C # 6.0:

catch(Exception ex) when (ex is SomeException || ex is OtherException)
{
    // Handle exception
}
17
23.05.2017 10:31:35
+1, это лучший ответ. Это лучше, чем верхний ответ, в основном потому, что нет return, хотя инвертирование условия также немного лучше.
DCShannon 18.03.2015 20:22:32
Я даже не думал об этом. Хороший улов, я добавлю его в список.
Stefan T 20.03.2015 00:35:32

Если вы можете обновить приложение до C # 6, вам повезет. В новой версии C # реализованы фильтры исключений. Таким образом, вы можете написать это:

catch (Exception ex) when (ex is FormatException || ex is OverflowException) {
    WebId = Guid.Empty;
}

Некоторые люди думают, что этот код такой же, как

catch (Exception ex) {                
    if (ex is FormatException || ex is OverflowException) {
        WebId = Guid.Empty;
    }
    throw;
}

Но это не так. На самом деле это единственная новая функция в C # 6, которую невозможно эмулировать в предыдущих версиях. Во-первых, повторный бросок означает больше накладных расходов, чем пропуск улова. Во-вторых, это не семантически эквивалентно. Новая функция сохраняет стек без изменений при отладке кода. Без этой функции аварийный дамп менее полезен или даже бесполезен.

Смотрите обсуждение этого на CodePlex . И пример, показывающий разницу .

74
1.04.2015 16:04:37
Бросок без исключения сохраняет стек, но "throw ex" перезапишет его.
Ivan 12.04.2017 13:18:59

В c # 6.0 Фильтры исключений - улучшения для обработки исключений

try
{
    DoSomeHttpRequest();
}
catch (System.Web.HttpException e)
{
    switch (e.GetHttpCode())
    {
        case 400:
            WriteLine("Bad Request");
        case 500:
            WriteLine("Internal Server Error");
        default:
            WriteLine("Generic Error");
    }
}
-23
20.05.2015 07:48:59
В этом примере не показано использование фильтров исключений.
user247702 27.05.2015 13:37:27
Это стандартный способ фильтрации исключений в c # 6.0
Kashif 27.05.2015 13:42:49
Посмотрите еще раз на то, что именно фильтры исключений. Вы не используете фильтр исключений в вашем примере. В этом ответе есть хороший пример за год до вашего.
user247702 27.05.2015 13:44:10
Примером фильтрации исключений будетcatch (HttpException e) when e.GetHttpCode() == 400 { WriteLine("Bad Request"; }
saluce 13.11.2015 16:29:11

в C # 6 рекомендуется использовать фильтры исключений, вот пример:

 try
 {
      throw new OverflowException();
 }
 catch(Exception e ) when ((e is DivideByZeroException) || (e is OverflowException))
 {
       // this will execute iff e is DividedByZeroEx or OverflowEx
       Console.WriteLine("E");
 }
19
7.10.2015 07:53:58

Если вы не хотите использовать ifзаявление в пределах catchобластей, в C# 6.0вы можете использовать Exception Filtersсинтаксис , который уже был поддержан СЕК в превью версии , но существовала только в VB.NET/ MSIL:

try
{
    WebId = new Guid(queryString["web"]);
}
catch (Exception exception) when (exception is FormatException || ex is OverflowException)
{
    WebId = Guid.Empty;
}

Этот код будет ловить Exceptionтолько когда это InvalidDataExceptionили ArgumentNullException.

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

static int a = 8;

...

catch (Exception exception) when (exception is InvalidDataException && a == 8)
{
    Console.WriteLine("Catch");
}

Обратите внимание, что в отличие от ifоператора внутри области catchвидимости, он Exception Filtersне может выдавать Exceptions, и когда он это делает, или когда условие не trueвыполняется catch, вместо него будет оцениваться следующее условие:

static int a = 7;

static int b = 0;

...

try
{
    throw new InvalidDataException();
}
catch (Exception exception) when (exception is InvalidDataException && a / b == 2)
{
    Console.WriteLine("Catch");
}
catch (Exception exception) when (exception is InvalidDataException || exception is ArgumentException)
{
    Console.WriteLine("General catch");
}

Выход: общий улов.

Когда их больше одного true Exception Filter- будет принят первый:

static int a = 8;

static int b = 4;

...

try
{
    throw new InvalidDataException();
}
catch (Exception exception) when (exception is InvalidDataException && a / b == 2)
{
    Console.WriteLine("Catch");
}
catch (Exception exception) when (exception is InvalidDataException || exception is ArgumentException)
{
    Console.WriteLine("General catch");
}

Выход: поймать.

И, как вы можете видеть из MSILэтого, код не переводится в ifоператоры, а в Filtersи Exceptionsне может быть выброшен из областей, отмеченных значком Filter 1и, Filter 2но фильтр, выбрасывающий значение, Exceptionбудет давать сбой, также последнее значение сравнения, помещенное в стек перед endfilterкомандой определит успех / неудачу фильтра ( Catch 1 XOR Catch 2 будет выполняться соответственно):

Фильтры исключений MSIL

Также конкретно Guidесть Guid.TryParseметод.

32
7.10.2015 18:37:42
+1 за показ нескольких фильтров и объяснение того, что происходит при использовании нескольких фильтров.
steven87vt 9.10.2018 13:59:44

Просто позвоните попробуйте и поймать дважды.

try
{
    WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
    WebId = Guid.Empty;
}
try
{
    WebId = new Guid(queryString["web"]);
}
catch (OverflowException)
{
    WebId = Guid.Empty;
}

Это так просто!

-15
29.07.2016 04:42:02
гм. это побеждает цель вопроса. Он задает этот вопрос, чтобы избавиться от дубликата кода. этот ответ добавляет больше повторяющегося кода.
James Esh 23.02.2017 21:40:12

То есть вы повторяете много кода в каждом переключателе исключений? Похоже, что извлечение метода было бы идеей бога, не так ли?

Итак, ваш код сводится к этому:

MyClass instance;
try { instance = ... }
catch(Exception1 e) { Reset(instance); }
catch(Exception2 e) { Reset(instance); }
catch(Exception) { throw; }

void Reset(MyClass instance) { /* reset the state of the instance */ }

Интересно, почему никто не заметил это дублирование кода.

С C # 6 у вас также есть фильтры исключений, как уже упоминалось другими. Таким образом, вы можете изменить код выше к этому:

try { ... }
catch(Exception e) when(e is Exception1 || e is Exception2)
{ 
    Reset(instance); 
}
6
23.05.2017 11:47:26
«Интересно, почему никто не заметил это дублирование кода». - что? Вся суть вопроса заключается в устранении дублирования кода.
Mark Amery 23.10.2017 22:03:24

Хотел добавить мой короткий ответ в эту уже длинную ветку. Кое-что, что не было упомянуто, является порядком приоритета операторов catch, более конкретно, вам необходимо знать область действия каждого типа исключения, которое вы пытаетесь перехватить.

Например, если вы используете исключение «catch-all» в качестве Exception, оно будет предшествовать всем остальным операторам catch, и вы, очевидно, получите ошибки компилятора, однако, если вы поменяете порядок, вы можете связать свои операторы catch (немного анти-паттерна, я думаю, ) вы можете поместить тип исключения «ловить все » внизу, и это будет захватывать любые исключения, которые не учитывались выше в вашем блоке try..catch:

            try
            {
                // do some work here
            }
            catch (WebException ex)
            {
                // catch a web excpetion
            }
            catch (ArgumentException ex)
            {
                // do some stuff
            }
            catch (Exception ex)
            {
                // you should really surface your errors but this is for example only
                throw new Exception("An error occurred: " + ex.Message);
            }

Я настоятельно рекомендую людям просмотреть этот документ MSDN:

Иерархия исключений

4
16.04.2017 15:51:04

Это классическая проблема, с которой сталкивается каждый разработчик C #.

Позвольте мне разбить ваш вопрос на 2 вопроса. Первый,

Могу ли я поймать несколько исключений одновременно?

Короче нет.

Что приводит к следующему вопросу,

Как избежать написания дублирующего кода, если я не могу перехватить несколько типов исключений в одном блоке catch ()?

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

  1. Инициализируйте WebId в качестве запасного значения.
  2. Создайте новый Guid во временной переменной.
  3. Установите для WebId полностью созданную временную переменную. Сделайте это последним утверждением блока try {}.

Итак, код выглядит так:

try
{
    WebId = Guid.Empty;
    Guid newGuid = new Guid(queryString["web"]);
    // More initialization code goes here like 
    // newGuid.x = y;
    WebId = newGuid;
}
catch (FormatException) {}
catch (OverflowException) {}

Если выдается какое-либо исключение, то WebId никогда не устанавливается на половину созданного значения и остается Guid.Empty.

Если создание запасного значения стоит дорого, а сброс значения намного дешевле, я бы переместил код сброса в его собственную функцию:

try
{
    WebId = new Guid(queryString["web"]);
    // More initialization code goes here.
}
catch (FormatException) {
    Reset(WebId);
}
catch (OverflowException) {
    Reset(WebId);
}
7
15.11.2017 20:27:32
Это хорошее «экологическое кодирование», т.е. вы заранее обдумываете свой код и объем данных и не допускаете утечки половины обработанных значений. Приятно будет следовать этой схеме, спасибо Джеффри!
Tahir Khalid 5.03.2018 00:44:07

С C # 7 ответ от Michael Stum может быть улучшен при сохранении читабельности оператора switch:

catch (Exception ex)
{
    switch (ex)
    {
        case FormatException _:
        case OverflowException _:
            WebId = Guid.Empty;
            break;
        default:
            throw;
    }
}
22
5.01.2018 11:43:42
Это должен быть принятый ответ по состоянию на 2018 г. ИМХО.
MemphiZ 9.08.2018 15:38:51
Использование ответа Mat J whenгораздо более элегантно / уместно, чем переключение.
rgoliveira 6.11.2018 15:03:21
@rgoliveira: Я согласен, что для случая, заданного в вопросе, ответ от Mat J является более элегантным и уместным. Тем не менее, становится трудно читать, если у вас есть другой код, который вы хотите выполнить в зависимости от типа исключения или если вы действительно хотите использовать экземпляр исключения. Все эти сценарии могут рассматриваться одинаково с этим оператором switch.
Fabian 7.11.2018 13:47:38
@Fabian "если у вас есть другой код, который вы хотите выполнить в зависимости от типа исключения, или если вы действительно хотите использовать экземпляр исключения", то вы просто создаете другой catchблок, или вам все равно придется его приводить .. По моему опыту, throw;в вашем catchблоке, вероятно, запах кода.
rgoliveira 7.11.2018 16:05:41
@rgoliveira: Использование броска в блоке catch можно в некоторых случаях, см. ссылку . Поскольку оператор case на самом деле использует ссылку сопоставления с образцом, вам не нужно приводить его, если вы заменяете ссылку оператора отбрасывания (подчеркивание) на имя переменной. Не поймите меня неправильно, я согласен с вами, что фильтры исключений - более чистый способ сделать это, но несколько блоков catch добавляют много фигурных скобок.
Fabian 8.11.2018 17:30:43

Может быть, постараться сохранить свой код простым, например, помещая общий код в метод, как вы это делали бы в любой другой части кода, не входящей в предложение catch?

Например:

try
{
    // ...
}
catch (FormatException)
{
    DoSomething();
}
catch (OverflowException)
{
    DoSomething();
}

// ...

private void DoSomething()
{
    // ...
}

Просто, как бы я это сделал, пытаясь найти простой, красивый шаблон

4
23.01.2018 14:02:59

Фильтры исключений теперь доступны в c # 6+. Ты можешь сделать

try
{
       WebId = new Guid(queryString["web"]);
}
catch (Exception ex) when(ex is FormatException || ex is OverflowException)
{
     WebId = Guid.Empty;
}

В C # 7.0+ вы также можете комбинировать это с сопоставлением с шаблоном

try
{
   await Task.WaitAll(tasks);
}
catch (Exception ex) when( ex is AggregateException ae &&
                           ae.InnerExceptions.Count > tasks.Count/2)
{
   //More than half of the tasks failed maybe..? 
}
104
11.07.2019 14:05:43
Этот метод предпочтителен не только потому, что он прост и понятен, но также не должен раскручивать стек, если условия не выполняются, что обеспечивает лучшую производительность и диагностическую информацию по сравнению с повторной обработкой.
joe 29.07.2019 04:46:14

Здесь стоит упомянуть. Вы можете ответить на несколько комбинаций (Exception error и exception.message).

Я столкнулся с сценарием сценария использования при попытке привести управляющий объект в сетку данных с таким содержимым, как TextBox, TextBlock или CheckBox. В этом случае возвращенное исключение было таким же, но сообщение изменилось.

try
{
 //do something
}
catch (Exception ex) when (ex.Message.Equals("the_error_message1_here"))
{
//do whatever you like
} 
catch (Exception ex) when (ex.Message.Equals("the_error_message2_here"))
{
//do whatever you like
} 
2
7.12.2018 15:14:45

Я хочу предложить кратчайший ответ (еще один функциональный стиль ):

        Catch<FormatException, OverflowException>(() =>
            {
                WebId = new Guid(queryString["web"]);
            },
            exception =>
            {
                WebId = Guid.Empty;
            });

Для этого вам нужно создать несколько перегрузок метода «Catch», аналогично System.Action:

    [DebuggerNonUserCode]
    public static void Catch<TException1, TException2>(Action tryBlock,
        Action<Exception> catchBlock)
    {
        CatchMany(tryBlock, catchBlock, typeof(TException1), typeof(TException2));
    }

    [DebuggerNonUserCode]
    public static void Catch<TException1, TException2, TException3>(Action tryBlock,
        Action<Exception> catchBlock)
    {
        CatchMany(tryBlock, catchBlock, typeof(TException1), typeof(TException2), typeof(TException3));
    }

и так далее сколько угодно. Но вам нужно сделать это один раз, и вы можете использовать его во всех своих проектах (или, если вы создали пакет nuget, мы могли бы использовать его тоже).

И реализация CatchMany:

    [DebuggerNonUserCode]
    public static void CatchMany(Action tryBlock, Action<Exception> catchBlock,
        params Type[] exceptionTypes)
    {
        try
        {
            tryBlock();
        }
        catch (Exception exception)
        {
            if (exceptionTypes.Contains(exception.GetType())) catchBlock(exception);
            else throw;
        }
    }

PS Я не поставил нулевые проверки для простоты кода, рассмотрите возможность добавления проверки параметров.

ps2 Если вы хотите вернуть значение из catch, необходимо сделать те же методы Catch, но с параметрами Return и Func вместо Action.

0
27.05.2019 14:18:24