c# tutorial Mehrere Ausnahmen gleichzeitig erfassen?




c# task tutorial (20)

Es wird davon abgeraten, System.Exception einfach zu fangen. Stattdessen sollten nur die "bekannten" Ausnahmen abgefangen werden.

Nun führt dies manchmal zu unnötigem repetitivem Code, zum Beispiel:

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

Ich frage mich: Gibt es eine Möglichkeit, beide Ausnahmen zu WebId = Guid.Empty und den WebId = Guid.Empty Aufruf nur einmal aufzurufen?

Das angegebene Beispiel ist ziemlich einfach, da es nur eine GUID . Stellen Sie sich jedoch Code vor, bei dem Sie ein Objekt mehrfach ändern und wenn eine der Manipulationen auf eine erwartete Weise fehlschlägt, möchten Sie das object "zurücksetzen". Wenn es jedoch eine unerwartete Ausnahme gibt, möchte ich diese immer noch höher werfen.


Nicht in C # leider, da Sie einen Ausnahmefilter benötigen, um es zu tun, und C # diese MSIL-Funktion nicht verfügbar macht. VB.NET hat diese Fähigkeit jedoch, z

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

Was Sie tun könnten, ist eine anonyme Funktion zu verwenden, um Ihren fehlerhaften Code einzukapseln und dann in diesen spezifischen catch-Blöcken aufzurufen:

Action onError = () => WebId = Guid.Empty;
try
{
    // something
}
catch (FormatException)
{
    onError();
}
catch (OverflowException)
{
    onError();
}

Update 2015-12-15: Siehe https://.com/a/22864936/1718702 für C # 6. Es ist ein sauberer und jetzt Standard in der Sprache.

Entwickelt für Leute, die eine elegantere Lösung wollen , um einmal zu fangen und Ausnahmen zu filtern, verwende ich eine Erweiterungsmethode wie unten gezeigt.

Ich hatte diese Erweiterung bereits in meiner Bibliothek, ursprünglich für andere Zwecke geschrieben, aber sie funktionierte nur perfekt für die Typüberprüfung von Ausnahmen. Plus, imho, es sieht sauberer aus als ein Bündel von || Aussagen. Im Gegensatz zur akzeptierten Antwort bevorzuge ich explizite Ausnahmebehandlung, so dass ex is ... unerwünschtes Verhalten hat, da abgeleitete Klassen ihren Elterntypen zugeordnet werden können.

Verwendung

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

IsAnyOf.cs-Erweiterung (siehe vollständiges Fehlerbehandlungsbeispiel für Abhängigkeiten)

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

Beispiel für die vollständige Fehlerbehandlung (Kopieren - Einfügen in neue Konsolenanwendung)

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

Two Sample NUnit Unit Tests

Matching behaviour for Exception types is exact (ie. A child IS NOT a match for any of its parent types).

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(Exception)))
                    throw;

                /*else... Handle other exception types, typically by logging to file*/
            }
        }
    }
}

With C# 7 the answer from Michael Stum can be improved while keeping the readability of a switch statement:

catch (Exception ex)
{
    switch (ex)
    {
        case FormatException _:
        case OverflowException _:
            WebId = Guid.Empty;
            break;
        default:
            throw;
    }
}

Wie wäre es mit

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

Wie andere bereits erwähnt haben, können Sie eine if Anweisung in Ihrem catch-Block haben, um festzustellen, was passiert. C # 6 unterstützt Exception Filter, daher wird Folgendes funktionieren:

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

Die MyFilter Methode könnte dann etwa so aussehen:

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

Alternativ kann dies auch inline geschehen (die rechte Seite der when-Anweisung muss nur ein boolescher Ausdruck sein).

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

Dies unterscheidet sich von der Verwendung einer if Anweisung innerhalb des catch Blocks. Die Verwendung von Ausnahmefiltern wird den Stapel nicht auflösen.

Sie können Visual Studio 2015 herunterladen, um dies zu überprüfen.

Wenn Sie Visual Studio 2013 weiterhin verwenden möchten, können Sie das folgende nuget-Paket installieren:

Install-Paket Microsoft.Net.Compiler

Zum Zeitpunkt des Schreibens wird dies Unterstützung für C # 6 enthalten.

Wenn Sie dieses Paket referenzieren, wird das Projekt mit der spezifischen Version der C # - und Visual Basic-Compiler erstellt, die im Paket enthalten sind, im Gegensatz zu jeder vom System installierten Version.


Since I felt like these answers just touched the surface, I attempted to dig a bit deeper.

So what we would really want to do is something that doesn't compile, say:

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

The reason we want this is because we don't want the exception handler to catch things that we need later on in the process. Sure, we can catch an Exception and check with an 'if' what to do, but let's be honest, we don't really want that. (FxCop, debugger issues, uglyness)

So why won't this code compile - and how can we hack it in such a way that it will?

If we look at the code, what we really would like to do is forward the call. However, according to the MS Partition II, IL exception handler blocks won't work like this, which in this case makes sense because that would imply that the 'exception' object can have different types.

Or to write it in code, we ask the compiler to do something like this (well it's not entirely correct, but it's the closest possible thing I guess):

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

The reason that this won't compile is quite obvious: what type and value would the '$exception' object have (which are here stored in the variables 'e')? The way we want the compiler to handle this is to note that the common base type of both exceptions is 'Exception', use that for a variable to contain both exceptions, and then handle only the two exceptions that are caught. The way this is implemented in IL is as 'filter', which is available in VB.Net.

To make it work in C#, we need a temporary variable with the correct 'Exception' base type. To control the flow of the code, we can add some branches. Hier geht:

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

The obvious disadvantages for this are that we cannot re-throw properly, and -well let's be honest- that it's quite the ugly solution. The uglyness can be fixed a bit by performing branch elimination, which makes the solution slightly better:

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

That leaves just the 're-throw'. For this to work, we need to be able to perform the handling inside the 'catch' block - and the only way to make this work is by an catching 'Exception' object.

At this point, we can add a separate function that handles the different types of Exceptions using overload resolution, or to handle the Exception. Both have disadvantages. To start, here's the way to do it with a helper function:

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

And the other solution is to catch the Exception object and handle it accordingly. The most literal translation for this, based on the context above is this:

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

So to conclude:

  • If we don't want to re-throw, we might consider catching the right exceptions, and storing them in a temporary.
  • If the handler is simple, and we want to re-use code, the best solution is probably to introduce a helper function.
  • If we want to re-throw, we have no choice but to put the code in a 'Exception' catch handler, which will break FxCop and your debugger's uncaught exceptions.

EDIT: Ich stimme mit anderen überein, die sagen, dass ab C # 6.0 Exception-Filter jetzt eine vollkommen gute Möglichkeit sind: catch (Exception ex) when (ex is ... || ex is ... )

Abgesehen davon, dass ich immer noch das One-Long-Line-Layout hasse und persönlich den Code wie folgt auslege. Ich denke, das ist so funktional wie ästhetisch, weil ich glaube, dass es das Verständnis verbessert. Manche mögen dem widersprechen:

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

ORIGINAL:

Ich weiß, ich bin ein wenig zu spät zur Party hier, aber heiliger Rauch ...

Diese Art von Duplikate kopieren eine frühere Antwort, aber wenn Sie wirklich eine gemeinsame Aktion für mehrere Ausnahmetypen durchführen und das Ganze im Rahmen der einen Methode sauber und ordentlich halten wollen, warum nicht einfach ein Lambda verwenden / closure / inline-Funktion, um so etwas wie das Folgende zu tun? Ich meine, die Chancen stehen gut, dass Sie am Ende realisieren, dass Sie diese Schließung nur als eine separate Methode sehen wollen, die Sie überall einsetzen können. Aber dann wird es super einfach sein, ohne den Rest des Codes strukturell zu ändern. Recht?

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

Ich kann nicht anders, als mich zu wundern ( Warnung: ein wenig Ironie / Sarkasmus voraus), warum um alles in der Welt gehen, um im Grunde nur das Folgende zu ersetzen:

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

... mit ein paar verrückten Variationen dieses nächsten Code-Geruchs, ich meine zum Beispiel, nur um so zu tun, als würden Sie ein paar Tastenanschläge speichern.

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

Weil es sicherlich nicht automatisch lesbarer ist.

Zugegeben, ich habe die drei identischen Instanzen von /* write to a log, whatever... */ return; aus dem ersten Beispiel.

Aber das ist mein Standpunkt. Ihr habt von Funktionen / Methoden gehört, oder? Ernst. Schreibe eine allgemeine ErrorHandler Funktion und rufe sie von jedem catch-Block ab.

Wenn Sie mich fragen, ist das zweite Beispiel (mit den Schlüsselwörtern if und is ) in der Wartungsphase Ihres Projekts sowohl deutlich weniger lesbar als auch signifikant fehleranfälliger.

Die Wartungsphase für alle, die mit der Programmierung relativ neu sind, wird 98,7% oder mehr der Gesamtlebensdauer Ihres Projekts ausmachen, und der arme Schmutzer, der die Wartung durchführt, wird mit ziemlicher Sicherheit jemand anderer als Sie sein. Und es besteht eine sehr gute Chance, dass sie 50% ihrer Zeit damit verbringen, Ihren Namen zu verfluchen.

Und natürlich bellt FxCop Sie an und Sie müssen Ihrem Code auch ein Attribut hinzufügen, das genau mit dem laufenden Programm zip hat, und es ist nur da, um FxCop zu sagen, ein Problem zu ignorieren, das in 99,9% der Fälle vollständig ist korrigieren in der Markierung. Und, sorry, ich könnte mich irren, aber wird das Attribut "Ignorieren" nicht tatsächlich in Ihrer App kompiliert?

Würde der gesamte if Test in einer Zeile besser lesbar sein? Ich denke nicht. Ich meine, ich habe schon vor langer Zeit einen anderen Programmierer vehement streiten lassen, dass mehr Code in einer Zeile "schneller laufen" würde. Aber natürlich war er total verrückt. Er versuchte ihm zu erklären (mit einem ernsthaften Gesicht - was herausfordernd war), wie der Interpreter oder Compiler diese lange Linie in diskrete Ein-Befehl-pro-Zeile-Anweisungen zerlegen würde - im Wesentlichen identisch mit dem Ergebnis, wenn er vorausgegangen wäre Er machte den Code lesbar, anstatt zu versuchen, den Compiler auszutricksen. Er hatte keinerlei Auswirkungen auf ihn. Aber ich schweife ab.

Wie viel weniger lesbar wird das, wenn Sie drei weitere Ausnahmearten hinzufügen, ab jetzt ein oder zwei Monate? (Antwort: es wird viel weniger lesbar).

Einer der wichtigsten Punkte ist, dass der wichtigste Punkt der Formatierung des Textquellcodes, den wir jeden Tag betrachten, darin besteht, anderen Menschen wirklich, wirklich klar zu machen, was tatsächlich passiert, wenn der Code läuft. Weil der Compiler den Quellcode in etwas völlig anderes verwandelt und sich nicht mehr um Ihren Code-Formatierungsstil kümmert. Also alles in allem auch total scheiße.

Sag nur ...

// super sucks...
catch( Exception ex )
{
    if ( ex is FormatException || ex is OverflowException || ex is ArgumentNullException )
    {
        // write to a log, whatever...
        return;
    }
    throw;
}

Wanted to added my short answer to this already long thread. Something that hasn't been mentioned is the order of precedence of the catch statements, more specifically you need to be aware of the scope of each type of exception you are trying to catch.

For example if you use a "catch-all" exception as Exception it will preceed all other catch statements and you will obviously get compiler errors however if you reverse the order you can chain up your catch statements (bit of an anti-pattern I think) you can put the catch-all Exception type at the bottom and this will be capture any exceptions that didn't cater for higher up in your try..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);
            }

I highly recommend folks review this MSDN document:

Exception Hierarchy


Catch System.Exception und schalten Sie die Typen ein

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

    throw;
}

Maybe try to keep your code simple such as putting the common code in a method, as you would do in any other part of the code that is not inside a catch clause?

Z.B:

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

// ...

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

Just how I would do it, trying to find the simple is beautiful pattern


Joseph Daigles Antwort ist eine gute Lösung, aber ich fand die folgende Struktur etwas aufgeräumter und weniger fehleranfällig.

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

    // Handle exception
}

Es gibt einige Vorteile, den Ausdruck zu invertieren:

  • Eine Rückgabeanweisung ist nicht erforderlich
  • Der Code ist nicht verschachtelt
  • Es besteht keine Gefahr, die "throw" oder "return" Aussagen zu vergessen, die in Josephs Lösung vom Ausdruck getrennt sind.

Es kann sogar zu einer einzigen Linie verdichtet werden (wenn auch nicht sehr hübsch)

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

    // Handle exception
}

Bearbeiten: Die Ausnahme-Filterung in C # 6.0 wird die Syntax ein wenig sauberer und bringt eine Reihe von anderen Vorteilen gegenüber jeder aktuellen Lösung. (Vor allem den Stapel unbeschadet lassen)

Hier ist, wie das gleiche Problem mit C # 6.0 Syntax aussehen würde:

catch(Exception ex) when (ex is SomeException || ex is OtherException)
{
    // Handle exception
}

Vorsicht und Warnung: Noch eine andere Art, funktionaler Stil.

Was in der Verknüpfung steht, beantwortet Ihre Frage nicht direkt, aber es ist trivial, sie so auszudehnen, dass sie wie folgt aussieht:

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

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

(Im Grunde eine weitere leere Catch Überladung, die sich selbst zurückgibt)

Die größere Frage ist, warum . Ich denke nicht, dass die Kosten hier überwiegen :)


Just call the try and catch twice.

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

It is just that Simple!!


So you´re repeating lots of code within every exception-switch? Sounds like extracting a method would be god idea, doesn´t it?

So your code comes down to this:

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

I wonder why no-one noticed that code-duplication.

From C#6 you furthermore have the exception-filters as already mentioned by others. So you can modify the code above to this:

try { ... }
catch(Exception e) when(e is Exception1 || e is Exception2)
{ 
    Reset(instance); 
}

This is a classic problem every C# developer faces eventually.

Let me break your question into 2 questions. The first,

Can I catch multiple exceptions at once?

In short, no.

Which leads to the next question,

How do I avoid writing duplicate code given that I can't catch multiple exception types in the same catch() block?

Given your specific sample, where the fall-back value is cheap to construct, I like to follow these steps:

  1. Initialize WebId to the fall-back value.
  2. Construct a new Guid in a temporary variable.
  3. Set WebId to the fully constructed temporary variable. Make this the final statement of the try{} block.

So the code looks like:

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

If any exception is thrown, then WebId is never set to the half-constructed value, and remains Guid.Empty.

If constructing the fall-back value is expensive, and resetting a value is much cheaper, then I would move the reset code into its own function:

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

In C # 6 empfiehlt es sich, Exception-Filter zu verwenden, hier ein Beispiel:

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

Wenn Sie Ihre Anwendung auf C # 6 aktualisieren können, haben Sie Glück. Die neue C # -Version hat Exception-Filter implementiert. So können Sie das schreiben:

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

Manche Leute denken, dass dieser Code derselbe ist

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

Aber es ist nicht. Eigentlich ist dies das einzige neue Feature in C # 6, das in früheren Versionen nicht emuliert werden kann. Erstens bedeutet ein erneuter Wurf mehr Aufwand, als den Fang zu überspringen. Zweitens ist es nicht semantisch äquivalent. Die neue Funktion bewahrt den Stapel intakt, wenn Sie Ihren Code debuggen. Ohne diese Funktion ist der Crash-Dump weniger nützlich oder sogar nutzlos.

Siehe dazu eine roslyn.codeplex.com/discussions/541301 . Und ein Beispiel, das den Unterschied zeigt .


Note that I did find one way to do it, but this looks more like material for The Daily WTF :

catch (Exception ex)
{
    switch (ex.GetType().Name)
    {
        case "System.FormatException":
        case "System.OverflowException":
            WebId = Guid.Empty;
            break;
        default:
            throw;
    }
}

@ Micheal

Leicht überarbeitete Version Ihres Codes:

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

String Vergleiche sind hässlich und langsam.


Die akzeptierte Antwort scheint akzeptabel zu sein, außer dass CodeAnalysis / FxCop sich darüber beschweren wird, dass es einen allgemeinen Ausnahmetyp erfasst.

Es scheint auch, dass der "Ist" -Operator die Leistung leicht verschlechtert.

CA1800: Wenn Sie nicht unnötigerweise casten , sollten Sie "das Ergebnis des 'als' Operator 'testen", aber wenn Sie das tun, schreiben Sie mehr Code, als wenn Sie jede Ausnahme separat abfangen.

Wie auch immer, hier ist was ich tun würde:

bool exThrown = false;

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

if (exThrown)
{
    // Something else
}




exception-handling