Frage

Es ist mutlos einfach System.Exception zu fangen. Stattdessen wird nur die „bekannten“ Ausnahmen gefangen werden sollen.

Nun, dies führt manchmal zu unnötigen repetitiven 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, beiden Ausnahmen zu fangen und nur den WebId = Guid.Empty Anruf ruft einmal

Das gegebene Beispiel ist ziemlich einfach, wie es nur ein GUID . Aber stellen Sie Code, in dem Sie ein Objekt mehrmals ändern, und wenn einer der Manipulationen in einer erwarteten Weise scheitern, wollen Sie „Reset“, um die object. Wenn es jedoch eine unerwartete Ausnahme ist, möchte ich noch, dass höher werfen.

War es hilfreich?

Lösung

Fang System.Exception und Schalter auf den Typen

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

    throw;
}

Andere Tipps

EDIT: Ich mit anderen überein, die sagen, dass, wie von C # 6.0, Ausnahmefilter sind jetzt ein völlig in Ordnung Weg zu gehen: catch (Exception ex) when (ex is ... || ex is ... )

Außer, dass ich immer noch Art von Hass, die eine Long-Line-Layouts und persönlich würde den Code Layout wie folgt aus. Ich denke, das als funktional wie ästhetisch ist, da ich glaube, dass es das Verständnis verbessert. Einige können nicht zustimmen:

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

ORIGINAL:

Ich weiß, ich bin ein wenig spät, um die Partei hier, aber heiliger Rauch ...

Schneiden gerade auf die Jagd, diese Art von Duplikaten eine frühere Antwort, aber wenn Sie wirklich eine gemeinsame Aktion für mehrere Ausnahmetypen ausführen möchten und halten das Ganze sauber und ordentlich im Rahmen des einen Verfahrens, warum nicht einfach verwenden, um eine Lambda / Verschluss / Inline-Funktion in etwa wie folgt zu tun? Ich meine, sind die Chancen ziemlich gut, dass Sie die Realisierung am Ende feststellen, dass Sie nur wollen, ein separates Verfahren, dass Schließung, die Sie überall nutzen können. Aber dann wird es super einfach sein, das zu tun, ohne tatsächlich strukturell den Rest des Codes zu ändern. Richtig?

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, aber frage mich ( Warnung: ein wenig Ironie / Sarkasmus voraus) helfen, warum auf der Erde gehen, um all diese Bemühungen im Grunde ersetzen Sie einfach die folgenden:

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

... mit einiger verrückten Variation dieses nächsten Code Geruch, meine ich beispielsweise nur so zu tun, dass Sie ein paar Tastenanschläge sind zu 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 sicher nicht automatisch besser lesbar.

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

Aber das ist irgendwie mein Punkt. Y'all hat von Funktionen / Methoden gehört, nicht wahr? Ernsthaft. Schreibe eine gemeinsame ErrorHandler Funktion und, wie, nennen es von jedem catch-Block.

Wenn Sie mich fragen, das zweite Beispiel (mit dem if und is keywords) ist sowohl deutlich weniger lesbar, und gleichzeitig deutlich mehr fehleranfällig während der Erhaltungsphase des Projektes.

Die Erhaltungsphase, für alle, die Programmierung relativ neu sein könnte, wird 98,7% oder mehr der gesamten Lebensdauer des Projekts umfassen, und der arme Trottel die Wartung zu tun ist fast sicher jemand anderes sein würde, als Sie . Und es gibt eine sehr gute Chance, dass sie 50% ihrer Zeit bei der Arbeit verbringen Ihren Namen verfluchen.

Und natürlich bei Ihnen FxCop bellt und so haben Sie auf auch Fügen Sie ein Attribut, um Ihren Code, der genau mit dem laufenden Programm zu tun hat zip und ist nur es FxCop zu sagen, um ein Problem zu ignorieren, dass in 99,9% der Fälle es in dem Markieren von völlig korrekt ist. Und, sorry, ich falsch sein könnte, ist aber nicht, dass „ignorieren“ Attribut tatsächlich am Ende in Ihrer Anwendung kompiliert?

Would setzt den gesamten if Test auf einer Linie macht es besser lesbar? Ich glaube nicht. Ich meine, ich habe eine andere Programmierer vehement einmal vor langer Zeit argumentieren haben, dass in einer Zeile mehr Code setzen würde es „schneller laufen.“ Aber natürlich war er stark raving Nüsse. Der Versuch, ihn (mit ernstem Gesicht - die herausfordernd war) zu erklären, wie der Interpreter oder Compiler, dass die lange Linie auseinander in diskrete Ein-Befehl-per-line-Anweisungen brechen würde - im Wesentlichen identisch mit dem Ergebnis, wenn er voraus gegangen war und gerade den Code lesbar stattdessen out-klug, den Compiler zu versuchen - hatte keinen Einfluss auf ihn zu löschen. Aber ich schweife ab.

Wie viel weniger lesbar bekommt dies, wenn Sie drei weitere Ausnahmetypen hinzufügen, ein Monat oder zwei ab jetzt? . (Antwort: es wird ein Los weniger lesbar)

Einer der wichtigsten Punkte, wirklich, ist, dass die meisten von dem Punkt der Formatierung des Text Quellcode, den wir alle an jedem Tag auf der Suche ist es wirklich zu machen, wirklich offensichtlich zu anderen Menschen, was tatsächlich geschieht, wenn die Code ausgeführt wird.Da der Compiler macht den Quellcode in etwas ganz anderes und konnte nicht weniger kümmern uns um Ihre Code-Formatierung Stil. Also all-on-one-line völlig saugt auch.

Nur zu sagen ...

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

Wie andere haben darauf hingewiesen, können Sie eine if Anweisung in Ihrem Catch-Block haben, um zu bestimmen, was los ist. C # 6 unterstützt Ausnahmefilter, so dass die folgende funktioniert:

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

Die MyFilter Methode dann wie folgt aussehen könnte:

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

Alternativ kann diese Inline alles getan werden (die rechte Seite der when-Anweisung muss nur ein boolscher Ausdruck sein).

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

Dies unterscheidet sich von der Verwendung eines if Anweisung aus dem catch Block, Ausnahmefilter mit nicht entspannen Stapel zu erhalten.

Sie können Visual Studio 2015 herunterladen dies überprüfen aus.

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

  

Install-Package Microsoft.Net.Compilers

Zum Zeitpunkt des Schreibens dieser Unterstützung ist für C # 6.

  

Dieses Paket Referenzierung wird das Projekt veranlassen, dass mithilfe der integrierten die   spezifische Version der C # und Visual Basic-Compiler in dem enthaltenen   Paket, wie auf jedes System installierte Version gegenüber.

Nicht in C # leider, wie Sie eine Ausnahme Filter, es zu tun bräuchten und C # aussetzen nicht, dass die Funktion von MSIL. VB.NET tut, obwohl diese Fähigkeit haben, z.

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

Was könnten Sie tun, ist eine anonyme Funktion verwenden, um Ihren on-Fehlercode einkapseln, und es dann in diesen spezifischen catch-Blöcken nennen:

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

Aus Gründen der Vollständigkeit, da .NET 4.0 der Code neu geschrieben als:

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

TryParse wirft nie Ausnahmen und gibt false zurück, wenn Format ist falsch, das Setzen von webid Guid.Empty.


Da C # 7 können Sie vermeiden, eine Variable in einer separaten Zeile Einführung:

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

Sie können auch Methoden schaffen für das Parsen der Rückkehr Tupeln, die noch in .NET Framework nicht verfügbar sind ab Version 4.6:

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

Und sie wie folgt verwendet werden:

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

Als nächstes nutzlos Aktualisierung dieser nutzlose Antwort kommt, wenn Rückbau von Out-Parameter in C # implementiert ist 12:)

Wenn Sie die Anwendung in C # 6 aktualisieren können Sie Glück haben. Die neue C # -Version hat Exception Filter implementiert. So kann man schreiben:

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

Einige Leute denken, dieser Code das gleiche wie

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

Aber es ist nicht. Eigentlich ist dies die einzige neue Funktion in C # 6, die nicht möglich ist, in früheren Versionen zu emulieren. Zunächst wird ein Wiederwurf bedeutet mehr Aufwand als den Fang zu überspringen. Zweitens ist es nicht semantisch äquivalent. Die neue Funktion erhält der Stapel intakt, wenn Sie Ihren Code debuggen. Ohne diese Funktion ist das Crash-Dump weniger nützlich oder sogar nutzlos.

Diskussion über diese auf CodePlex . Und ein Beispiel den Unterschied zeigt.

Ausnahme Filter sind nun in c # 6+ verfügbar. Sie können tun,

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

In C # 7.0 und höher, können Sie dies kombiniert mit Mustern zu passenden

try
{
   await Task.WaitAll(tasks);
}
catch (Exception ex) when( ex is AggregateException ae )
{
   //do something with members of ae, say ae.InnerExceptions
}

Wenn Sie keine if Anweisung innerhalb der catch Bereiche verwenden möchten, in C# 6.0 können Sie verwenden Exception Filters Syntax , die bereits von der CLR in Vorschauen Versionen unterstützt wurde aber gab es nur in VB.NET / MSIL:

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

Dieser Code wird die Exception nur fangen, wenn es ein InvalidDataException oder ArgumentNullException.

Eigentlich kann man im Grunde jede Bedingung innerhalb dieser when Klausel setzen:

static int a = 8;

...

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

Beachten Sie, dass im Gegensatz zu einer if Anweisung innerhalb des Anwendungsbereichs der catch, Exception Filters nicht Exceptions werfen kann, und wenn sie es tun, oder wenn die Bedingung nicht ist true, wird der nächste catch Zustand statt bewertet werden:

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

Ausgabe:. Allgemein Fang

Wenn es mehr als ein true Exception Filter wird - die erste akzeptiert werden:

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

Ausgabe:. Fang

Und wie können Sie in der MSIL sehen Sie den Code wird nicht Aussagen if, sondern Filters und Exceptions können nicht von den Bereichen mit Filter 1 und Filter 2 aber den Filter wirft den Exception markiert werfen wird nicht statt, auch die zuletzt gedrückt Vergleichswert auf den Stapel vor dem endfilter Befehl wird den Erfolg / Misserfolg des Filters (Catch 1 XOR Catch 2 wird ausgeführt entsprechend) bestimmen:

 Ausnahmefilter MSIL

Auch speziell Guid hat die Guid.TryParse Methode.

Die akzeptierte Antwort scheint akzeptabel, außer dass CodeAnalysis / FxCop wird über die Tatsache beklagen, dass es eine allgemeine Ausnahmetyp zu kontrollieren.

Auch scheint es, das „ist“ Bedienerleistung verschlechtert leicht könnte.

CA1800: Verwirf nicht unnötig sagt zu „prüfen, testet das Ergebnis des‚als‘Operator statt“, aber wenn Sie das tun, werden Sie als mehr Code schreiben, wenn Sie separat jede Ausnahme zu fangen.

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
}

in C # 6 der empfohlene Ansatz ist Exception Filter zu verwenden, hier ist 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");
 }

Mit C # 7 der Antwort von Michael Stum kann verbessert werden, während die Lesbarkeit einer switch-Anweisung zu halten:

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

Dies ist eine Variante von Matts Antwort (ich glaube, dass dies etwas sauberer ist) ... verwenden, um eine Methode:

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

    WebId = Guid.Empty;
}

Alle anderen Ausnahmen werden geworfen und der Code WebId = Guid.Empty; nicht getroffen werden. Wenn Sie Ihr Programm zum Absturz keine anderen Ausnahmen wollen, fügen Sie einfach diese nach der anderen zwei Fänge:

...
catch (Exception)
{
     // something, if anything
     return; // only need this if you follow the example I gave and put it all in a method
}

Joseph Daigle Antwort- ist eine gute Lösung, aber ich fand die folgende Struktur ein wenig aufgeräumter und weniger fehleranfällig.

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

    // Handle exception
}

Es gibt einige Vorteile der Invertierung der Ausdruck:

  • Eine return-Anweisung ist nicht erforderlich
  • Der Code wird nicht verschachtelt
  • Es gibt kein Risiko, der ‚werfen‘ des Vergessens oder ‚Rückkehr‘ Aussagen, die in Josephs Lösung aus dem Ausdruck getrennt sind.

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

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

    // Handle exception
}

Edit: Die Ausnahme Filterung in C # 6.0 mit einer Reihe weiterer Vorteile wird die Syntax ein wenig sauberer machen und kommt über alle aktuellen Lösung. (Vor allem dem Verlassen der Stapel unversehrt)

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

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

@Micheal

Slightly überarbeitete Version des 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.

Wie wäre es

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

ausdrücklich darauf hingewiesen und gewarnt: Noch eine andere Art , funktionaler Stil.

Was ist in der Verbindung Ihre Frage nicht direkt beantworten, aber es ist trivial, es zu erweitern, um wie folgt aussehen:

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

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

(Im Grunde eine weitere leere Catch Überlastung bereitzustellen, die selbst zurückgibt)

Die größere Frage zu diesem ist Warum . Ich glaube nicht, die Kosten überwiegen die Verstärkung hier:)

catch (Exception ex)
{
    if (!(
        ex is FormatException ||
        ex is OverflowException))
    {
        throw;
    }
    Console.WriteLine("Hello");
}

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

für Menschen, die eine Getriebe elegantere Lösung wollen einmal und Filter Ausnahmen fangen, verwende ich eine Erweiterungsmethode, wie gezeigt, unten.

Ich hatte bereits diese Erweiterung in meiner Bibliothek, die ursprünglich für andere Zwecke geschrieben, aber es funktionierte einfach perfekt für type auf Ausnahmen zu prüfen. Plus, imho, sieht es sauberer als ein Bündel von || Aussagen. Auch anders als der akzeptierte Antwort, ziehe ich es explizite Ausnahmebehandlung so ex is ... undesireable Verhalten hatte wie schließt man Klassen zugeordnet werden können, um dort übergeordnete Typen).

Verwendung

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

IsAnyOf.cs Extension (Vollständige Fehlerbehandlung Beispiel 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;
        }
    }
}

Voll Fehler Beispiel Handling (Copy-Paste auf neue Konsole app)

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

Zwei Beispiel NUnit Unit-Tests

Matching-Verhalten für Exception Typen ist genau (dh. Ein Kind ist nicht ein Spiel für eine seiner Muttertyp).

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

Da fühlte ich mich wie diese Antworten nur die Oberfläche berühren, ich versuchte, ein wenig tiefer zu graben.

Also, was wir wirklich tun möchte, ist etwas, das nicht kompiliert, sagen:

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

Der Grund, warum wir dies wollen, weil wir die Dinge nicht die Ausnahme-Handler wollen fangen, die wir später im Prozess benötigen. Sicher, können wir eine Ausnahme fangen und überprüfen Sie mit ein ‚wenn‘, was zu tun, aber seien wir ehrlich, dass wir nicht wirklich wollen, dass. (FxCop, Debugger Probleme, uglyness)

Warum wird nicht dieser Code Kompilierung - und wie können wir es so zu hacken, dass es wird

Wenn wir den Code anschauen, was wir tun möchten wirklich vorwärts der Anruf. Doch nach der MS-Partition II, IL Ausnahmebehandlungsblocks nicht arbeiten, was in diesem Fall sinnvoll ist, denn das ist, dass das ‚Ausnahme‘ Objekt unterschiedliche Typen haben kann bedeuten würde.

Oder es im Code zu schreiben, wir die Compiler bitten, etwas zu tun (naja, es ist nicht ganz richtig, aber es ist in der Nähe möglich, was ich denke):

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

Der Grund, dass diese kompilieren wird nicht ganz klar ist: welche Art und Wert würde die ‚$ exception‘ bezweckt (die in den Variablen ‚e‘ hier gespeichert werden)? Die Art, wie wir die Compiler wollen, dies zu umgehen ist zu beachten, dass die gemeinsame Basis Art der beiden Ausnahmen ist ‚Exception‘, dass die beiden Ausnahmen enthält für eine Variable verwenden, und dann behandelt nur die beiden Ausnahmen, die gefangen werden. Die Art und Weise dies in IL implementiert ist, ist als 'Filter', die in VB.Net zur Verfügung.

Um es in C # funktioniert, brauchen wir eine temporäre Variable mit dem richtigen ‚Exception‘ Basistyp. Um den Fluss des Codes zu steuern, können wir einige Zweige hinzufügen. 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.");

Die offensichtlichen Nachteile dafür sind, dass wir nicht richtig werfen wieder kann und -well wir honest- sein, dass es durchaus die hässliche Lösung ist. Die uglyness kann ein bisschen, indem Zweig Beseitigung festgelegt werden, die die Lösung macht etwas besser:

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

Das läßt nur den 'Re-Wurf'. Damit dies funktioniert, müssen wir in der Lage sein, die Handhabung in der ‚fangen‘ Block durchzuführen - und der einzige Weg, um diese Arbeit zu machen, ist durch einen Fang ‚Exception‘ Objekt.

An diesem Punkt können wir eine eigene Funktion hinzufügen, die die verschiedenen Arten von Ausnahmen mit Überladungsauflösung behandelt, oder die Ausnahme zu behandeln. Beide haben Nachteile. Um zu beginnen, hier ist der Weg, um es mit einer Hilfsfunktion zu tun:

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

Und die andere Lösung ist das Exception-Objekt zu fangen und sie entsprechend zu behandeln. Die wörtliche Übersetzung für das, basierend auf dem Kontext oben ist dies:

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 schließen:

  • Wenn wir wollen, nicht erneut werfen wir vielleicht die richtigen Ausnahmen betrachten zu kontrollieren und sie in einem temporären Speichern.
  • Wenn die Prozedur einfach, und wir wollen wieder verwenden Code, die beste Lösung ist wahrscheinlich eine Hilfsfunktion einzuführen.
  • Wenn wir wieder werfen wollen, haben wir keine andere Wahl, als den Code in einem ‚Exception‘ catch-Handler zu setzen, die brechen FxCop und Ihr Debugger nicht abgefangene Ausnahmen.

Dies ist ein klassisches Problem jedes C # Entwickler schließlich steht.

Lassen Sie mich brechen Sie Ihre Frage in zwei Fragen. Die erste,

Kann ich mehrere Ausnahmen auf einmal fangen?

Kurz gesagt, nein.

Was zur nächsten Frage führt,

Wie vermeide ich das Schreiben doppelte Code gegeben, dass ich nicht mehrere Ausnahmetypen in dem gleichen Fang () blockieren fangen kann?

Aufgrund Ihrer spezifischen Probe, wobei der Rückfallwert billig zu konstruieren, Ich mag die folgenden Schritte ausführen:

  1. initialisieren webid auf den Rückfallwert.
  2. Erstellen Sie eine neue GUID in einer temporären Variablen.
  3. Set webid in die vollständig aufgebaut temporären Variable. Macht dies die letzte Anweisung des try {} -Block.

So ist der Code wie folgt aussieht:

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

Wenn eine Ausnahme ausgelöst wird, dann webid wird nie auf den Halb rechnerisch ermittelten Wert gesetzt und bleibt Guid.Empty.

, um den Rückfallwert Wenn die Konstruktion teuer ist, und einen Wert zurückzusetzen ist viel billiger, dann würde ich den Reset-Code in seine eigene Funktion verschieben:

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

Also du bist viel Code in jeder Ausnahme-Schalter wiederholen? Klingt wie ein Verfahren zu extrahieren Idee wäre Gott, spielt keine es?

So Ihr Code kommt unten zu diesem:

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

Ich frage mich, warum niemand, dass Code-Duplizierung bemerkt.

Von C # 6 Sie weiterhin die Ausnahme-Filter haben wie bereits von anderen erwähnt. So können Sie den obigen Code in das ändern:

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

Will meine kurze Antwort auf diesen schon langen Thread hinzugefügt. Etwas, das nicht ist die Rangordnung der Fangmeldungen erwähnt hat, genauer gesagt müssen Sie sich bewusst den Umfang jeder Art von Ausnahme sein, Sie zu fangen versuchen.

Wenn Sie zum Beispiel eine „catch-all“ Ausnahme als verwenden Exception wird es alle anderen Fangmeldungen vorausgehen, und Sie werden offensichtlich Compiler-Fehler jedoch erhalten, wenn Sie die Reihenfolge umkehren können Sie verketten Ihren Fang Anweisungen (Bit eines anti-Muster glaube ich) Sie die catch-all Ausnahme Typen am Boden setzen können, und dies wird Capture-Ausnahmen, die nicht für höher in Ihrem Versuch gerecht zu haben .. 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);
            }

Ich empfehle, Leute in diesem MSDN-Dokument überprüfen:

Ausnahmehierarchie

Vielleicht versuchen Sie den Code einfach zu halten wie der gemeinsamen Code in einer Methode setzen, wie Sie in einem anderen Teil des Codes tun würden, die nicht in einer catch-Klausel ist?

Z. B:.

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

// ...

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

Wie ich es tun würde, versucht die zu finden ist einfach schön Muster

Beachten Sie, dass ich einen Weg finden, haben es zu tun, aber das sieht eher wie Material für The Daily WTF :

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

Es ist erwähnenswert hier. Sie können zu den mehrfachen Kombinationen (Ausnahmefehler und Exception.Message) reagieren.

Ich lief in ein Szenario Anwendungsfall bei dem Versuch, Steuerobjekt in einem Datagrid zu werfen, entweder mit Inhalt als TextBox, TextBlock- oder CheckBox. In diesem Fall war die zurückgegebene Ausnahme die gleiche, aber die Botschaft verändert.

try
{
 //do something
}
catch (Exception ex) when (ex.Message.Equals("the_error_message1_here"))
{
//do whatever you like
} 
catch (Exception ex) when (ex.Message.Equals("the_error_message2_here"))
{
//do whatever you like
} 

Ich möchte kürzeste Antwort vorschlagen (noch eine funktional ):

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

Für das Sie mehr „Catch“ Methode Überlastungen, ähnlich wie System.Action erstellen müssen:

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

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

und so weiter, so viele, wie Sie wollen. Aber Sie müssen es einmal tun und man kann es in allen Projekten (oder, wenn Sie ein nuget Paket haben wir es benutzen konnten).

Und CatchMany Umsetzung:

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

P. S. Ich habe nicht null prüft Code Einfachheit setzen, betrachten Parameter Validierungen hinzuzufügen.

p.s.2 Wenn Sie einen Wert aus dem Fang zurückkehren wollen, ist es notwendig, gleiche Fangmethoden zu tun, sondern mit Renditen und Func statt Aktion in Parametern.

Sie einfach den Versuch nennen und zweimal fangen.

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

Es ist nur so einfach !!

In c # 6.0, Exception Filter ist es, Verbesserungen für die Ausnahmebehandlung

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");
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top