Złapać wiele wyjątków na raz?

Odradza się po prostu łapać System.Exception. Zamiast tego należy wyłapywać tylko" znane " wyjątki.

Teraz, to czasami prowadzi do nieneccessary powtarzalny kod, na przykład:

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

Zastanawiam się: czy jest sposób na wyłapanie obu wyjątków i wywołanie WebId = Guid.Empty tylko raz?

Podany przykład jest dość prosty, ponieważ jest to tylko GUID. Ale wyobraź sobie kod, w którym modyfikujesz obiekt wiele razy, a jeśli jedna z manipulacji nie powiedzie się w oczekiwany sposób, chcesz aby "zresetować" object. Jeśli jednak pojawi się nieoczekiwany wyjątek, nadal chcę rzucić to wyżej.

Author: Mark Amery, 2008-09-26

26 answers

Złap System.Exception i włącz typy

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

    throw;
}
 1825
Author: Joseph Daigle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-26 01:53:35

EDIT: zgadzam się z innymi, którzy mówią, że od C # 6.0 filtry WYJĄTKÓW są teraz doskonałym sposobem na przejście: catch (Exception ex) when (ex is ... || ex is ... )

Z wyjątkiem tego, że nadal trochę nienawidzę układu jednej długiej linii i osobiście układałbym kod w następujący sposób. Myślę, że jest to tak funkcjonalne, jak estetyczne, ponieważ uważam, że poprawia zrozumienie. Niektórzy mogą się nie zgodzić:

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

Oryginalny:

Wiem, że jestem trochę spóźniony na imprezę tutaj, ale Święty dym...

Przechodząc od razu do sedna, ten rodzaj duplikuje wcześniejszą odpowiedź, ale jeśli naprawdę chcesz wykonać wspólną akcję dla kilku typów wyjątków i zachować porządek w ramach jednej metody, dlaczego po prostu nie użyć funkcji lambda / closure / inline, aby zrobić coś takiego jak poniżej? Mam na myśli, że szanse są całkiem spore, że skończysz zdając sobie sprawę, że po prostu chcesz, aby to zamknięcie było oddzielną metodą, którą możesz wykorzystać wszędzie. Ale wtedy będzie bardzo łatwo to zrobić bez faktycznej zmiany reszty kodu strukturalnie. Prawda?

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 ); }
}

Nie mogę pomóc, ale zastanawiam się (Ostrzeżenie: trochę ironii/sarkazmu przed nami) dlaczego na Boga dołożyć wszelkich starań, aby w zasadzie zastąpić następujące:

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

...z jakąś szaloną wariacją tego następnego zapachu kodu, mam na myśli przykład, tylko udawać, że zapisujesz kilka naciśnięć klawiszy.

// 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;
}

Ponieważ z pewnością nie jest automatycznie bardziej czytelne.

Przyznaję, zostawiłem trzy identyczne instancje /* write to a log, whatever... */ return; z pierwszego przykładu.

Ale o to mi chodzi. Słyszeliście o funkcjach / metodach, prawda? Poważnie. Napisz wspólną funkcję ErrorHandler i wywołaj ją z każdego bloku catch.

Według mnie, drugi przykład (ze słowami kluczowymi if i is) jest zarówno znacznie mniej czytelny, jak i jednocześnie znacznie bardziej podatny na błędy w fazie konserwacji Twojego komputera projekt.

[11]}Faza konserwacji, dla każdego, kto może być stosunkowo nowy w programowaniu, będzie obejmować 98,7% lub więcej ogólnego okresu życia Twojego projektu, a biedny palant wykonujący konserwację prawie na pewno będzie kimś innym niż ty. I jest bardzo duża szansa, że poświęcą 50% swojego czasu na przeklinanie Twojego imienia.

I oczywiście FxCop szczeka na ciebie, więc musisz również Dodaj atrybut do kodu to ma dokładnie zip do czynienia z uruchomionym programem i jest tam tylko po to, aby powiedzieć FxCop, aby zignorował problem, który w 99,9% przypadków jest całkowicie poprawny w oznaczaniu. I, przepraszam, mogę się mylić, ale czy ten atrybut "ignoruj" nie kończy się w rzeczywistości skompilowany do Twojej aplikacji?

Czy umieszczenie całego if testu w jednej linii uczyni go bardziej czytelnym? Nie sądzę. Miałem kiedyś innego programistę, który stanowczo argumentował, że umieszczenie większej ilości kodu na jednej linijce sprawi, że " ruszy szybciej."Ale oczywiście był szalonym świrem. Próba wyjaśnienia mu (z prostą twarzą-co było wyzwaniem) jak interpreter lub kompilator podzieliłby tę długą linijkę na dyskretne instrukcje dla każdej linijki-zasadniczo identyczne do wyniku, gdyby poszedł naprzód i po prostu uczynił kod czytelnym zamiast próbować prześcignąć kompilatora-nie miało na niego żadnego wpływu. Ale dygresję.

Ile mniej czytelne robi to po dodaniu jeszcze trzy typy WYJĄTKÓW, za miesiąc czy dwa? (Odpowiedź: dostaje Nr serii mniej czytelne).

Jedną z głównych kwestii jest to, że większość formatowania tekstowego kodu źródłowego, na który wszyscy patrzymy każdego dnia, polega na tym, aby uczynić to naprawdę, naprawdę oczywiste dla innych ludzi, co się dzieje, gdy kod działa. Ponieważ kompilator zmienia kod źródłowy w coś zupełnie innego i nie obchodzi go formatowanie kodu styl. Więc all-on-one-line też jest do bani.

Tak tylko mówię...
// super sucks...
catch( Exception ex )
{
    if ( ex is FormatException || ex is OverflowException || ex is ArgumentNullException )
    {
        // write to a log, whatever...
        return;
    }
    throw;
}
 392
Author: Craig,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-14 20:38:54

Jak zauważyli inni, możesz mieć if oświadczenie wewnątrz swojego bloku, aby określić, co się dzieje. C # 6 obsługuje filtry WYJĄTKÓW, więc będą działać:

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

Metoda MyFilter może wtedy wyglądać mniej więcej tak:

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

Alternatywnie, można to wszystko zrobić inline (prawa strona instrukcji when musi być wyrażeniem logicznym).

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

Różni się to od użycia instrukcji if z bloku catch, używając filtry WYJĄTKÓW nie rozwiążą stosu.

Możesz pobrać Visual Studio 2015, aby to sprawdzić.

Jeśli chcesz nadal korzystać z Visual Studio 2013, możesz zainstalować następujący pakiet nuget:

Install-Pakiet Microsoft.Net. Kompilatory

W momencie pisania tego tekstu będzie to obejmować wsparcie dla C # 6.

Odwołanie się do tego pakietu spowoduje, że projekt zostanie zbudowany przy użyciu specjalna wersja kompilatory C# i Visual Basic zawarte w pakiet, w przeciwieństwie do każdej zainstalowanej wersji systemu.

 258
Author: Joe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-06 18:11:12

Nie w C# niestety, ponieważ do tego potrzebny jest filtr WYJĄTKÓW, A C# nie ujawnia tej funkcji MSIL. VB.NET posiada jednak taką możliwość, np.

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

Możesz użyć funkcji anonimowej do hermetyzacji kodu błędu, a następnie wywołać go w określonych blokach przechwytywania:

Action onError = () => WebId = Guid.Empty;
try
{
    // something
}
catch (FormatException)
{
    onError();
}
catch (OverflowException)
{
    onError();
}
 183
Author: Greg Beech,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-25 21:03:57

Ze względu na kompletność, ponieważ . NET 4.0 kod można przepisać jako:

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

TryParse nigdy nie wyrzuca wyjątków i zwraca false, jeśli format jest nieprawidłowy, ustawiając WebId na Guid.Empty.


Od C# 7 można uniknąć wprowadzania zmiennej w osobnej linii:

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

Możesz także utworzyć metody do parsowania zwracających krotek, które nie są jeszcze dostępne w. NET Framework od wersji 4.6:

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

I używać ich jak to:

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

Następna bezużyteczna aktualizacja do tej bezużytecznej odpowiedzi pojawia się, gdy dekonstrukcja out-parametrów jest zaimplementowana w C # 12. :)

 124
Author: Athari,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-18 12:40:01

Jeśli możesz uaktualnić swoją aplikację do C # 6 masz szczęście. Nowa wersja C# ma zaimplementowane filtry WYJĄTKÓW. Więc możesz to napisać:

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

Niektórzy myślą, że ten kod jest taki sam jak

catch (Exception ex) {                
    if (ex is FormatException || ex is OverflowException) {
        WebId = Guid.Empty;
    }
    throw;
}
Ale nie jest. W rzeczywistości jest to jedyna nowa funkcja w C# 6, której nie można emulować w poprzednich wersjach. Po pierwsze, ponowny rzut oznacza więcej nad głową niż pomijanie haczyka. Po drugie, nie jest to równoznaczne semantycznie. Nowa funkcja zachowuje stos nienaruszony, gdy jesteś debugowanie kodu. Bez tej funkcji zrzut awaryjny jest mniej przydatny lub nawet bezużyteczny.

Zobacz dyskusję na ten temat na CodePlex . I przykład pokazujący różnicę .

 64
Author: Maniero,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-01 16:04:37

Jeśli nie chcesz używać if instrukcji w zakresie catch , W C# 6.0 możesz użyć składni Exception Filters który był już obsługiwany przez CLR w wersjach przedpremierowych, ale istniał tylko w VB.NET/MSIL:

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

Ten kod złapie Exception tylko wtedy, gdy jest InvalidDataException lub ArgumentNullException.

Właściwie, można umieścić w zasadzie każdy warunek wewnątrz when klauzuli:

static int a = 8;

...

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

Zauważ, że w przeciwieństwie do if stwierdzenia wewnątrz zakresu catch, Exception Filters nie można wyrzucić Exceptions, A gdy to zrobią, lub gdy warunek nie jest true, następny warunek catch zostanie oceniony zamiast tego:

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");
}

Wyjście: ogólny chwyt.

Gdy jest więcej niż jeden true Exception Filter - pierwszy z nich zostanie zaakceptowany:

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");
}

Wyjście: Catch.

I jak widać w MSIL kod nie jest tłumaczony na if, ale na Filters i Exceptions nie może być wyrzucany z obszarów oznaczonych Filter 1 i Filter 2 ale filtr rzucający Exception nie powiedzie się, również ostatnia wartość porównania przesunięta na stos przed poleceniem endfilter określi sukces / niepowodzenie filtra (Catch 1 XOR Catch 2 wykonam odpowiednio):

Exception Filters MSIL

ponadto, w szczególności Guid ma Guid.TryParse metoda.

 28
Author: Tamir Vered,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-07 18:37:42

Zaakceptowana odpowiedź wydaje się akceptowalna, tyle że CodeAnalysis / FxCop będzie narzekać na fakt, że łapie ogólny typ wyjątku.

Wydaje się również, że operator "jest" może nieznacznie obniżyć wydajność.

CA1800: nie rzucaj niepotrzebnie mówi, aby "rozważyć przetestowanie wyniku operatora' as 'zamiast", ale jeśli to zrobisz, będziesz pisać więcej kodu niż Jeśli złapiesz każdy wyjątek osobno.

/ Align = "left" / Zrobiłbym:
bool exThrown = false;

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

if (exThrown)
{
    // Something else
}
 19
Author: Matt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-09 14:09:55

To jest wariant odpowiedzi Matta (czuję, że jest to trochę czystsze)...użyj metody:

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

    WebId = Guid.Empty;
}

Inne wyjątki zostaną rzucone, a kod WebId = Guid.Empty; nie zostanie trafiony. Jeśli nie chcesz, aby inne wyjątki zawieszały Twój program, po prostu dodaj to po pozostałych dwóch łapach:

...
catch (Exception)
{
     // something, if anything
     return; // only need this if you follow the example I gave and put it all in a method
}
 18
Author: bsara,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-11 20:52:50

@Micheal

Lekko poprawiona wersja twojego kodu:

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

Porównania łańcuchów są brzydkie i powolne.

 17
Author: FlySwat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-25 21:16:23

W C# 6 zalecane jest używanie filtrów WYJĄTKÓW, oto przykład:

 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");
 }
 17
Author: SHM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-07 07:53:58

Odpowiedź Josepha Daigle ' a jest dobrym rozwiązaniem, ale uważam, że poniższa struktura jest nieco uporządkowana i mniej podatna na błędy.

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

    // Handle exception
}

Istnieje kilka zalet odwrócenia wyrażenia:

  • deklaracja zwrotu nie jest konieczna
  • kod nie jest zagnieżdżony
  • nie ma ryzyka zapomnienia o wypowiedziach "throw" lub "return", które w rozwiązaniu Josepha są oddzielone od wyrażenia.

Może być nawet zagęszczony do jednej linii (choć niezbyt Ładna)

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

    // Handle exception
}

Edit: Filtrowanie WYJĄTKÓW w C# 6.0 sprawi, że składnia będzie nieco czystsza i będzie dostarczana z wieloma innymi korzyściami w stosunku do dowolnego obecnego rozwiązania. (przede wszystkim pozostawienie stosu bez szwanku)

Oto jak wyglądałby ten sam problem używając składni C # 6.0:

catch(Exception ex) when (ex is SomeException || ex is OtherException)
{
    // Handle exception
}
 16
Author: Stefan T,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:35

A może

try
{
    WebId = Guid.Empty;
    WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
}
catch (OverflowException)
{
}
 13
Author: Maurice,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-22 15:58:32

Z C # 7 odpowiedź Michaela Stuma może być poprawiona przy zachowaniu czytelności instrukcji switch:

catch (Exception ex)
{
    switch (ex)
    {
        case FormatException _:
        case OverflowException _:
            WebId = Guid.Empty;
            break;
        default:
            throw;
    }
}
 13
Author: Fabian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-05 11:43:42

Filtry WYJĄTKÓW są teraz dostępne w c # 6+. You can do

try
{
       WebId = new Guid(queryString["web"]);
}
catch (Exception ex) when(ex is FormatException || ex is OverflowException)
{
     WebId = Guid.Empty;
}
 13
Author: Mat J,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-11 13:12:05

Ostrzegam i ostrzegam: Kolejny rodzaj, funkcjonalny styl.

To, co jest w linku, nie odpowiada bezpośrednio na twoje pytanie, ale trywialne jest rozszerzenie go tak, aby wyglądało:

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

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

(zasadniczo podaj kolejne puste Catch przeciążenie, które zwraca się samo)

Większe pytanie brzmi dlaczego . Nie sądzę, aby koszt przewyższał zysk tutaj:)

 12
Author: nawfal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-18 11:28:30
catch (Exception ex)
{
    if (!(
        ex is FormatException ||
        ex is OverflowException))
    {
        throw;
    }
    Console.WriteLine("Hello");
}
 12
Author: Konstantin Spirin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-30 11:15:16

Aktualizacja 2015-12-15: Zobacz https://stackoverflow.com/a/22864936/1718702 dla C # 6. Jest czystszym i standardowym językiem.

Nastawiony na ludzi, którzy chcą bardziej eleganckie rozwiązanie aby złapać raz i filtrować wyjątki, używam metody rozszerzenia, jak pokazano poniżej.

Miałem już to rozszerzenie w mojej bibliotece, pierwotnie napisane do innych celów, ale działało idealnie do type sprawdzania WYJĄTKÓW. No i imho, wygląda czyściej niż kilka || wypowiedzi. Ponadto, w przeciwieństwie do zaakceptowanej odpowiedzi, preferuję obsługę wyraźnych WYJĄTKÓW, więc ex is ... miało niepożądane zachowanie, ponieważ klasy derrived są przypisywane do typów nadrzędnych).

Użycie

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

IsAnyOf.rozszerzenie cs (zobacz pełny przykład obsługi błędów Dla zależności)

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;
        }
    }
}

Przykład pełnej obsługi błędów (Kopiuj-Wklej do nowej aplikacji konsoli)

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));
        }
    }
}

Dwie Przykładowe Jednostki NUnit Testy

Dopasowanie zachowania dla typów Exception jest dokładne (tj. Dziecko nie pasuje do żadnego z jego typów rodzicielskich).

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*/
            }
        }
    }
}
 10
Author: HodlDwon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:35

Ponieważ czułem, że te odpowiedzi dotykają powierzchni, próbowałem kopać trochę głębiej.

Więc to, co naprawdę chcemy zrobić, to coś, co nie kompiluje się, powiedzmy:

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

Chcemy tego, ponieważ nie chcemy, aby obsługa wyjątków przechwytywała rzeczy, których potrzebujemy później w procesie. Oczywiście, możemy złapać wyjątek i sprawdzić za pomocą "Jeśli", co robić, ale bądźmy szczerzy, tak naprawdę tego nie chcemy. (FxCop, debugger issues, uglyness)

Więc dlaczego czy ten kod się nie skompiluje - i jak go zhakować w taki sposób, że będzie?

[6]}jeśli spojrzymy na kod, to naprawdę chcielibyśmy przekierować połączenie. Jednak, zgodnie z MS Partition II, bloki obsługi wyjątków IL nie będą działać w ten sposób, co w tym przypadku ma sens, ponieważ oznaczałoby to, że obiekt 'exception' może mieć różne typy.

Lub napisać go w kodzie, prosimy kompilator, aby zrobił coś takiego (cóż, nie jest to do końca poprawne, ale to najbliższa możliwa rzecz chyba):

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

Powód, dla którego nie będzie kompilacji jest dość oczywisty: jaki typ i wartość miałby obiekt '$exception' (który jest tutaj przechowywany w zmiennych 'e')? Sposób, w jaki chcemy, aby kompilator sobie z tym poradził, to zauważenie, że podstawowym typem obu wyjątków jest 'Exception', użyj tego, aby zmienna zawierała oba wyjątki, a następnie obsługuje tylko dwa wyjątki, które są przechwytywane. Sposób jest to zaimplementowane w IL jest jako "filtr", który jest dostępny w VB.Net.

Aby to działało w C#, potrzebujemy zmiennej tymczasowej o prawidłowym typie bazowym 'Exception'. Aby kontrolować przepływ kodu, możemy dodać kilka gałęzi. Idzie:

    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.");

Oczywistymi wadami tego rozwiązania jest to, że nie możemy odpowiednio rzucać i-bądźmy szczerzy-że jest to dość brzydkie rozwiązanie. Brzydota może być nieco poprawiona poprzez eliminację gałęzi, co sprawia, że rozwiązanie jest nieco lepsze: {]}

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 :-)");
}

Zostaje tylko 're-throw'. Aby to zadziałało, musimy być w stanie wykonać obsługę wewnątrz bloku 'catch' - a jedynym sposobem, aby to zadziałało, jest obiekt 'catching Exception'.

W tym momencie możemy dodać oddzielną funkcję, która obsługuje różne typy WYJĄTKÓW przy użyciu rozdzielczości przeciążenia, lub do obsługi wyjątku. Oba mają wady. Na początek, oto sposób, aby to zrobić za pomocą funkcji helper:

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.");

A innym rozwiązaniem jest złapanie obiektu Exception i obsługa go odpowiednio. Najbardziej dosłowne tłumaczenie tego, oparte na powyższym kontekście jest następujące:

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;
    }
}

Podsumowując:

  • jeśli nie chcemy ponownie rzucać, możemy rozważyć wyłapanie właściwych wyjątków i przechowywanie ich w tymczasowej formie.
  • Jeśli obsługa jest prosta i chcemy ponownie użyć kodu, najlepszym rozwiązaniem jest prawdopodobnie wprowadzenie funkcji pomocniczej.
  • jeśli chcemy ponownie rzucić, nie mamy wyboru, jak tylko umieścić kod w' Exception ' Catch handler, który będzie break FxCop i debuggera uncaught wyjątki.
 7
Author: atlaste,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-21 09:44:02

Więc powtarzasz dużo kodu w każdym przełączniku WYJĄTKÓW? Wygląda na to, że wydobycie metody byłoby ideą Boga, prawda?

Więc Twój kod sprowadza się do tego:

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 */ }
Zastanawiam się, dlaczego nikt nie zauważył tego duplikowania kodu.

Z C#6 masz dodatkowo exception-filters Jak już wspominali inni. Możesz więc zmodyfikować powyższy kod do tego:

try { ... }
catch(Exception e) when(e is Exception1 || e is Exception2)
{ 
    Reset(instance); 
}
 6
Author: HimBromBeere,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:26

Jest to klasyczny problem, z którym w końcu boryka się każdy programista C#.

Pozwól, że podzielę twoje pytanie na dwa pytania. Pierwszy,

Czy Mogę wyłapać kilka wyjątków jednocześnie?

Krótko mówiąc, nie.

Co prowadzi do następnego pytania,

Jak uniknąć pisania zduplikowanego kodu, ponieważ nie mogę złapać wielu typów wyjątków w tym samym bloku catch ()?

Biorąc pod uwagę twoją konkretną próbkę, gdzie wartość spadkowa jest tania w konstruowaniu, I jak wykonać te kroki:

  1. Zainicjalizuj WebId na wartość jesienną.
  2. Utwórz nowy identyfikator Guid w zmiennej tymczasowej.
  3. Ustaw WebId na w pełni skonstruowaną zmienną tymczasową. Niech to będzie ostateczna wypowiedź bloku try {}.

Więc kod wygląda następująco:

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) {}

Jeśli jakikolwiek wyjątek zostanie wyrzucony, to WebId nigdy nie zostanie ustawiony na wartość w połowie skonstruowaną i pozostanie Guid.Pusty.

Jeśli konstruowanie wartości zwrotnej jest kosztowne, a Resetowanie wartości jest znacznie tańsze, wtedy przeniosłbym kod reset do własnej funkcji:

try
{
    WebId = new Guid(queryString["web"]);
    // More initialization code goes here.
}
catch (FormatException) {
    Reset(WebId);
}
catch (OverflowException) {
    Reset(WebId);
}
 5
Author: Jeffrey Rennie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-15 20:27:32

Zauważ, że znalazłem jeden sposób, aby to zrobić, ale to wygląda bardziej jak materiał na The Daily WTF :

catch (Exception ex)
{
    switch (ex.GetType().Name)
    {
        case "System.FormatException":
        case "System.OverflowException":
            WebId = Guid.Empty;
            break;
        default:
            throw;
    }
}
 3
Author: Michael Stum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-09 14:07:34

Chciałem dodać moją krótką odpowiedź do tego już długiego wątku. Coś, co nie zostało wspomniane, to kolejność pierwszeństwa stwierdzeń catch, dokładniej musisz być świadomy zakresu każdego typu wyjątku, który próbujesz złapać.

Na przykład, jeśli użyjesz wyjątku "catch-all" jako wyjątek będzie poprzedzał wszystkie inne instrukcje catch i oczywiście otrzymasz błędy kompilatora, jednak jeśli odwrócisz kolejność, możesz połączyć swój catch instrukcje (trochę anty-wzorzec myślę) możesz umieścić Typ catch-all Exception na dole, a to będzie przechwytywanie wszelkich wyjątków, które nie zaspokajają wyżej w próbie.."catch block": {]}

            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);
            }

Gorąco polecam ludziom przejrzeć ten dokument MSDN:

Hierarchia WYJĄTKÓW

 3
Author: Tahir Khalid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-16 15:51:04

Może staraj się zachować prosty kod, taki jak umieszczenie wspólnego kodu w metodzie, tak jak w każdej innej części kodu, która nie znajduje się w klauzuli catch?

Np.:

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

// ...

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

Jak ja bym to zrobił, próbując znaleźćproste jest piękne Wzór

 2
Author: Żubrówka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-23 14:02:59

Po prostu zadzwoń do próby i złap dwa razy.

try
{
    WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
    WebId = Guid.Empty;
}
try
{
    WebId = new Guid(queryString["web"]);
}
catch (OverflowException)
{
    WebId = Guid.Empty;
}
To takie proste!!
 -13
Author: scholar guy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-29 04:42:02

W c# 6.0 filtry WYJĄTKÓW są ulepszane do obsługi wyjątków

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");
    }
}
 -20
Author: Kashif,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-20 07:48:59