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

Не рекомендуется просто ловить 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
Michael Stum
114054 репутация

26 ответов


  • 1819 рейтинг

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

    catch (Exception ex)            
    {                
        if (ex is FormatException || ex is OverflowException)
        {
            WebId = Guid.Empty;
            return;
        }
    
        throw;
    }
    
    ответ дан Joseph Daigle, с репутацией 35521, 25.09.2008
  • 389 рейтинг

    РЕДАКТИРОВАТЬ: Я согласен с другими, которые говорят, что, начиная с 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 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 лает на вас, и поэтому вам нужно , а также добавить атрибут в ваш код, который имеет точное zip, связанное с работающей программой, и предназначен только для того, чтобы 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;
    }
    
    ответ дан Craig, с репутацией 5168, 12.10.2013
  • 256 рейтинг

    Как уже отмечали другие, вы можете иметь оператор 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. Сеть. Компиляторы

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

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

    ответ дан Joe, с репутацией 4408, 4.04.2014
  • 183 рейтинг

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

    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();
    }
    
    ответ дан Greg Beech, с репутацией 97692, 25.09.2008
  • 124 рейтинг

    Ради полноты, начиная с . 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. :)

    ответ дан Athari, с репутацией 26935, 13.04.2013
  • 64 рейтинг

    Если вы можете обновить свое приложение до 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 . И пример , показывающий разницу .

    ответ дан Maniero, с репутацией 3711, 1.04.2015
  • 27 рейтинг

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

    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 будет выполняться соответственно):

    Exception Filters MSIL

    Также, в частности, Guid имеет метод Guid.TryParse .

    ответ дан Tamir Vered, с репутацией 8574, 7.10.2015
  • 19 рейтинг

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

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

    CA1800: не приводите к ненужным приведениям

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

    bool exThrown = false;
    
    try
    {
        // Something
    }
    catch (FormatException) {
        exThrown = true;
    }
    catch (OverflowException) {
        exThrown = true;
    }
    
    if (exThrown)
    {
        // Something else
    }
    

    ответ дан Matt, с репутацией 884, 30.07.2010
  • 18 рейтинг

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

    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
    }
    
    ответ дан bsara, с репутацией 5668, 31.08.2012
  • 17 рейтинг

    @Micheal

    Слегка исправленная версия вашего кода:

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

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

    ответ дан FlySwat, с репутацией 110216, 25.09.2008
  • 17 рейтинг

    в 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");
     }
    
    ответ дан SHM, с репутацией 1051, 4.10.2015
  • 16 рейтинг

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

    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
    }
    

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

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

    catch(Exception ex) when (ex is SomeException || ex is OtherException)
    {
        // Handle exception
    }
    
    ответ дан Stefan T, с репутацией 387, 8.12.2014
  • 13 рейтинг

    С C # 7 можно улучшить ответ от Майкла Стума , сохранив читаемость оператора switch:

    catch (Exception ex)
    {
        switch (ex)
        {
            case FormatException _:
            case OverflowException _:
                WebId = Guid.Empty;
                break;
            default:
                throw;
        }
    }
    
    ответ дан Fabian, с репутацией 169, 5.01.2018
  • 13 рейтинг

    Как насчет

    ?
    try
    {
        WebId = Guid.Empty;
        WebId = new Guid(queryString["web"]);
    }
    catch (FormatException)
    {
    }
    catch (OverflowException)
    {
    }
    
    ответ дан Maurice, с репутацией 25554, 25.09.2008
  • 12 рейтинг

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

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

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

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

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

    ответ дан nawfal, с репутацией 40852, 18.05.2013
  • 12 рейтинг
    catch (Exception ex)
    {
        if (!(
            ex is FormatException ||
            ex is OverflowException))
        {
            throw;
        }
        Console.WriteLine("Hello");
    }
    
    ответ дан Konstantin Spirin, с репутацией 11014, 7.01.2009
  • 11 рейтинг

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

    try
    {
           WebId = new Guid(queryString["web"]);
    }
    catch (Exception ex) when(ex is FormatException || ex is OverflowException)
    {
         WebId = Guid.Empty;
    }
    
    ответ дан Mat J, с репутацией 2843, 11.07.2018
  • 10 рейтинг

    Обновление 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 Extension (см. Пример полной обработки ошибок для зависимостей)

    namespace Common.FluentValidation
    {
        public static partial class Validate
        {
            /// 
    /// Validates the passed in parameter matches at least one of the passed in comparisons. ///
     
            /// 
            /// 
    Parameter to validate.
            /// 
    Values to compare against.
            /// True if a match is found.
            /// 
            public static bool IsAnyOf(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> TestActions = new List>()
                {
                    (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
        {
            /// 
    /// Validates the passed in parameter matches at least one of the passed in comparisons. ///
     
            /// 
            /// 
    Parameter to validate.
            /// 
    Values to compare against.
            /// True if a match is found.
            /// 
            public static bool IsAnyOf(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;
            }
    
            /// 
    /// Validates if any passed in parameter is equal to null. ///
     
            /// 
    Parameters to test for Null.
            /// True if one or more parameters are null.
            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
        {
            /// 
    /// Validates the passed in parameter is not null, throwing a detailed exception message if the test fails. ///
     
            /// 
    Parameter to validate.
            /// 
    Name of tested parameter to assist with debugging.
            /// 
            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
        {
            /// 
    /// Validates the passed in parameter is not null or an empty collection, throwing a detailed exception message if the test fails. ///
     
            /// 
            /// 
    Parameter to validate.
            /// 
    Name of tested parameter to assist with debugging.
            /// 
            /// 
            public static void CannotBeNullOrEmpty(this ICollection 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));
            }
    
            /// 
    /// Validates the passed in parameter is not null or empty, throwing a detailed exception message if the test fails. ///
     
            /// 
    Parameter to validate.
            /// 
    Name of tested parameter to assist with debugging.
            /// 
            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*/
                }
            }
        }
    }
    
    ответ дан HodlDwon, с репутацией 743, 27.11.2013
  • 7 рейтинг

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

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

    // 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. Сеть.

    Чтобы заставить его работать в 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 и необработанные исключения вашего отладчика.
    ответ дан atlaste, с репутацией 22210, 21.10.2014
  • 6 рейтинг

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

    Итак, ваш код сводится к следующему:

    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); 
    }
    
    ответ дан HimBromBeere, с репутацией 22134, 22.09.2016
  • 5 рейтинг

    Это классическая проблема, с которой сталкивается каждый разработчик 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. Пустой.

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

    try
    {
        WebId = new Guid(queryString["web"]);
        // More initialization code goes here.
    }
    catch (FormatException) {
        Reset(WebId);
    }
    catch (OverflowException) {
        Reset(WebId);
    }
    
    ответ дан Jeffrey Rennie, с репутацией 1533, 15.11.2017
  • 3 рейтинг

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

    catch (Exception ex)
    {
        switch (ex.GetType().Name)
        {
            case "System.FormatException":
            case "System.OverflowException":
                WebId = Guid.Empty;
                break;
            default:
                throw;
        }
    }
    
    ответ дан Michael Stum, с репутацией 114054, 25.09.2008
  • 3 рейтинг

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

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

                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:

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

    ответ дан Tahir Khalid, с репутацией 601, 16.04.2017
  • 2 рейтинг

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

    E. г. :

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

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

    ответ дан Żubrówka, с репутацией 455, 23.01.2018
  • -13 рейтинг

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

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

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

    ответ дан scholar guy, с репутацией 1, 29.07.2016
  • -20 рейтинг

    В с # 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");
        }
    }
    
    ответ дан Kashif, с репутацией 1291, 20.05.2015