Frage Mehrere Ausnahmen gleichzeitig erfassen?


Es ist entmutigt, einfach zu fangen System.Exception. 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 erfassen und nur die WebId = Guid.Empty einmal anrufen?

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


1709
2017-09-25 20:56


Ursprung


Antworten:


Fang System.Exception und schalte die Typen ein

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

    throw;
}

1788
2017-09-25 21:01



BEARBEITEN: Ich stimme mit anderen überein, die sagen, dass ab C # 6.0 Exception-Filter jetzt ein vollkommen guter Weg 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 in aller Welt zu all diesen Bemühungen 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 ein gemeinsames ErrorHandler Funktion und, wie, rufen Sie es von jedem catch-Block.

Wenn Sie mich fragen, das zweite Beispiel (mit der if und is keywords) ist 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 dich an und du musst es tun ebenfallsFügen Sie Ihrem Code ein Attribut hinzu, das genau mit dem laufenden Programm zu tun hat, und gibt nur an, dass FxCop ein Problem ignoriert, das in 99,9% der Fälle völlig korrekt ist. Und, sorry, ich könnte mich irren, aber wird das Attribut "Ignorieren" nicht tatsächlich in Ihrer App kompiliert?

Würde das ganze setzen if Test auf einer Zeile machen es lesbarer? 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 dies, wenn Sie drei weitere Ausnahmen hinzufügen, ein Monat oder zwei von jetzt an? (Antwort: es bekommt a Menge 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 ganz 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;
}

370
2017-10-12 00:24



Wie andere gezeigt haben, können Sie eine haben if Anweisung in Ihrem catch-Block, um festzustellen, was vor sich geht. C # 6 unterstützt Exception Filter, daher wird Folgendes funktionieren:

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

Das 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 eines if Aussage aus dem catch blockieren, mit Ausnahme-Filter wird nicht wickle den Stapel ab.

Sie können herunterladen Visual Studio 2015 um das 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 mithilfe von   bestimmte Version der C # - und Visual Basic - Compiler in der   Paket, im Gegensatz zu jeder installierten Systemversion.


239
2018-04-04 13:59



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

184
2017-09-25 21:03



Der Vollständigkeit halber, da .NET 4.0Der Code kann wie folgt umgeschrieben werden:

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

TryParse gibt niemals Ausnahmen aus und gibt false zurück, wenn das Format falsch ist und WebId auf gesetzt ist Guid.Empty.


Schon seit C # 7 Sie können vermeiden, eine Variable in eine separate Zeile einzufügen:

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

Sie können auch Methoden zum Analysieren zurückliegender Tupel erstellen, die in .NET Framework ab Version 4.6 noch nicht verfügbar sind:

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

Und benutze sie so:

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

Das nächste unnütze Update auf diese nutzlose Antwort kommt, wenn die Dekonstruktion der Out-Parameter in C # 12 implementiert wird. :)


122
2018-04-13 12:18



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.

Ein ... sehen Diskussion darüber auf CodePlex. Und ein Beispiel, das den Unterschied zeigt.


62
2018-04-01 12:29



Wenn Sie keine verwenden möchten if Aussage innerhalb der catch Bereiche, im C# 6.0 Sie können verwenden Exception Filters Syntax welches bereits in Previews Versionen von der CLR unterstützt wurde, aber 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 fängt die Exception nur wenn es ein ist InvalidDataException oder ArgumentNullException.

Eigentlich können Sie im Grunde alle Bedingungen in das System einbauen when Klausel:

static int a = 8;

...

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

Beachten Sie, dass im Gegensatz zu einem if Aussage innerhalb der catchder Umfang, Exception Filters kann nicht werfen Exceptionsund wenn sie es tun, oder wenn die Bedingung nicht ist true, der nächste catch Bedingung wird stattdessen ausgewertet:

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: Allgemeiner Fang.

Wenn es mehr als eins gibt true  Exception Filter - Der erste wird akzeptiert:

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 Sie in der sehen können MSIL Der Code ist nicht übersetzt in if Aussagen, aber zu Filters, und Exceptions kann nicht innerhalb der mit markierten Felder geworfen werden Filter 1 und Filter 2 aber der Filter wirft den Exception wird stattdessen fehlschlagen, auch der letzte Vergleichswert wird vor dem Stack auf den Stack geschoben endfilter Der Befehl bestimmt den Erfolg / Fehlschlag des Filters (Catch 1  XOR  Catch 2 wird entsprechend ausgeführt):

Exception Filters MSIL

Auch speziell Guid hat die Guid.TryParse Methode.


26
2017-10-07 17:31



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

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

CA1800: Nicht unnötig werfen sagt zu "erwägen, das Ergebnis des 'als' Operator 'zu testen", aber wenn Sie das tun, werden Sie mehr Code schreiben, als wenn Sie jede Ausnahme getrennt 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
}

19
2017-07-30 17:09



Dies ist eine Variante von Matts Antwort (ich fühle, dass dies ein bisschen sauberer ist) ... benutze eine Methode:

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

    WebId = Guid.Empty;
}

Alle anderen Ausnahmen werden ausgelöst und der Code WebId = Guid.Empty; wird nicht getroffen werden. Wenn Sie nicht möchten, dass andere Ausnahmen Ihr Programm zum Absturz bringen, fügen Sie dies NACH den anderen beiden Fängen hinzu:

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

18
2017-08-31 20:51



@ 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.


17
2017-09-25 21:01



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

17
2017-10-04 07:51