Domanda Cattura più eccezioni contemporaneamente?


Si scoraggia semplicemente a catturare System.Exception. Invece, dovrebbero essere catturate solo le eccezioni "conosciute".

Ora, questo a volte porta a codice ripetitivo non necessario, ad esempio:

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

Mi chiedo: c'è un modo per cogliere entrambe le eccezioni e chiamare solo il WebId = Guid.Empty chiamare una volta?

L'esempio dato è piuttosto semplice, in quanto è solo un GUID. Ma immagina il codice dove modifichi un oggetto più volte e se una delle manipolazioni fallisce in un modo previsto, vuoi "resettare" il object. Tuttavia, se c'è un'eccezione imprevista, voglio comunque lanciarla più in alto.


1709
2017-09-25 20:56


origine


risposte:


Catturare System.Exception e accendi i tipi

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

    throw;
}

1787
2017-09-25 21:01



MODIFICARE: Concordo con gli altri che stanno dicendo che, a partire da C # 6.0, i filtri delle eccezioni sono ora un modo perfetto per andare: catch (Exception ex) when (ex is ... || ex is ... )

A parte il fatto che ancora odio il layout a una riga lunga e personalmente depongo il codice come segue. Penso che questo sia tanto funzionale quanto estetico, dal momento che credo che migliori la comprensione. Alcuni potrebbero non essere d'accordo:

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

ORIGINALE:

So che sono un po 'in ritardo per la festa qui, ma santo fumo ...

Tagliando direttamente alla caccia, questo tipo di duplicati una risposta precedente, ma se si vuole veramente eseguire un'azione comune per diversi tipi di eccezioni e mantenere l'intero oggetto pulito e ordinato nell'ambito di un unico metodo, perché non usare solo un lambda / chiusura / funzione inline per fare qualcosa di simile al seguente? Voglio dire, ci sono buone probabilità che ti renderai conto che vuoi solo rendere quella chiusura un metodo separato che puoi utilizzare ovunque. Ma sarà semplicissimo farlo senza modificare effettivamente il resto del codice strutturalmente. Destra?

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

Non posso fare a meno di chiedermi (avvertimento: un po 'di ironia / sarcasmo avanti) perché sulla terra tutto questo sforzo fondamentalmente si limita a sostituire quanto segue:

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

... con qualche pazzesca variazione di questo prossimo odore di codice, voglio dire esempio, solo per far finta che stai salvando alcune sequenze di tasti.

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

Perché certamente non è automaticamente più leggibile.

Concesso, ho lasciato i tre esempi identici di /* write to a log, whatever... */ return; fuori dal primo esempio.

Ma è una specie del mio punto. Avete sentito parlare di funzioni / metodi, giusto? Sul serio. Scrivi un comune ErrorHandler funzione e, come, chiamarla da ogni blocco di cattura.

Se me lo chiedi, il secondo esempio (con il if e is parole chiave) è significativamente meno leggibile e contemporaneamente significativamente più soggetto a errori durante la fase di manutenzione del progetto.

La fase di manutenzione, per chiunque potrebbe essere relativamente nuovo alla programmazione, comprenderà il 98,7% o più della durata complessiva del progetto, e il povero schmuck che fa la manutenzione sarà quasi sicuramente qualcuno diverso da te. E c'è una buona possibilità che trascorrano il 50% del loro tempo sul lavoro a maledire il tuo nome.

E naturalmente FxCop ti abbaia e quindi devi ancheaggiungi un attributo al tuo codice che ha esattamente zip da fare con il programma in esecuzione, ed è lì solo per dire a FxCop di ignorare un problema che nel 99,9% dei casi è completamente corretto nel flagging. E, scusa, potrei sbagliarmi, ma questo attributo "ignora" non è compilato nella tua app?

Metterebbe l'intero if il test su una riga lo rende più leggibile? Io non la penso così Voglio dire, ho avuto un altro programmatore discusso con veemenza una volta tanto che mettere più codice su una riga lo renderebbe "correre più veloce". Ma ovviamente lui era un pazzo delirante. Cercando di spiegargli (con una faccia seria - che era una sfida) come l'interprete o il compilatore romperebbe quella lunga linea in istruzioni discrete a una istruzione per riga - essenzialmente identico al risultato se fosse andato avanti e ho solo reso il codice leggibile invece di cercare di rendere più intelligente il compilatore - non ha avuto alcun effetto su di lui. Ma sto divagando.

Quanto Di meno leggibile questo si ottiene quando aggiungi altri tre tipi di eccezioni, un mese o due da adesso? (Risposta: ottiene a lotto meno leggibile).

Uno dei punti principali, in realtà, è che la maggior parte del punto di formattazione del codice sorgente testuale che stiamo guardando tutti i giorni è di rendere davvero, molto ovvio agli altri esseri umani ciò che sta realmente accadendo quando il codice viene eseguito. Perché il compilatore trasforma il codice sorgente in qualcosa di completamente diverso e non potrebbe importare di meno del tuo stile di formattazione del codice. Quindi tutto su una sola riga fa schifo, anche.

Sto solo dicendo ...

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



Come altri hanno sottolineato, puoi avere un if dichiarazione all'interno del blocco catch per determinare cosa sta succedendo. C # 6 supporta i filtri di eccezione, quindi funzionerà quanto segue:

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

Il MyFilter il metodo potrebbe quindi assomigliare a questo:

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

In alternativa, questo può essere fatto tutto in linea (il lato destro dell'istruzione when deve essere solo un'espressione booleana).

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

Questo è diverso dall'usare un if affermazione dall'interno del catch blocco, usando i filtri delle eccezioni non lo farò srotolare la pila.

Puoi scaricare Visual Studio 2015 per verificarlo.

Se si desidera continuare a utilizzare Visual Studio 2013, è possibile installare il seguente pacchetto nuget:

Pacchetto di installazione Microsoft.Net.Compilers

Al momento della stesura, questo includerà il supporto per C # 6.

Facendo riferimento a questo pacchetto, il progetto verrà creato utilizzando il   versione specifica dei compilatori C # e Visual Basic contenuti in   pacchetto, a differenza di qualsiasi versione installata del sistema.


239
2018-04-04 13:59



Sfortunatamente non in C #, poiché è necessario un filtro di eccezione per farlo e C # non espone questa caratteristica di MSIL. Tuttavia, VB.NET ha questa capacità, ad es.

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

Quello che potresti fare è usare una funzione anonima per incapsulare il tuo codice di errore e poi chiamarlo in quei blocchi di cattura specifici:

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

184
2017-09-25 21:03



Per motivi di completezza, da allora .NET 4.0il codice può essere riscritto come:

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

TryParse non genera mai eccezioni e restituisce false se il formato è sbagliato, impostando WebId su Guid.Empty.


Da C # 7 puoi evitare di introdurre una variabile su una linea separata:

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

È anche possibile creare metodi per analizzare le tuple di ritorno, che non sono ancora disponibili in .NET Framework a partire dalla versione 4.6:

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

E usali in questo modo:

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

Il prossimo aggiornamento inutile a questa risposta inutile arriva quando la decostruzione dei parametri esterni è implementata in C # 12. :)


122
2018-04-13 12:18



Se puoi aggiornare la tua applicazione a C # 6 sei fortunato. La nuova versione di C # ha implementato i filtri di eccezione. Quindi puoi scrivere questo:

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

Alcune persone pensano che questo codice sia lo stesso di

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

Ma non lo è. In realtà questa è l'unica novità in C # 6 che non è possibile emulare nelle versioni precedenti. In primo luogo, un rilancio significa più spese generali che saltare la presa. In secondo luogo, non è semanticamente equivalente. La nuova funzione mantiene intatto lo stack quando si esegue il debug del codice. Senza questa funzionalità, il crash dump è meno utile o addirittura inutile.

Vedere un discussione su questo su CodePlex. E un esempio che mostra la differenza.


62
2018-04-01 12:29



Se non vuoi usare un if affermazione all'interno del catch ambiti, in C# 6.0 Puoi usare Exception Filters sintassi che era già supportato dal CLR nelle versioni anteprime ma esisteva solo in VB.NET/MSIL:

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

Questo codice catturerà il Exception solo quando è a InvalidDataException o ArgumentNullException.

In realtà, puoi mettere praticamente qualsiasi condizione al suo interno when clausola:

static int a = 8;

...

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

Si noti che al contrario di un if affermazione all'interno del catchportata, Exception Filters non posso buttare Exceptionse quando lo fanno, o quando la condizione non lo è true, il prossimo catch la condizione sarà valutata invece:

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

Risultato: catture generali.

Quando ce n'è più di uno true  Exception Filter - il primo sarà accettato:

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

Uscita: cattura.

E come puoi vedere nel MSIL il codice non è tradotto in if dichiarazioni, ma a Filters, e Exceptions non può essere lanciato all'interno delle aree contrassegnate con Filter 1 e Filter 2 ma il filtro che lancia il Exception fallirà invece, anche l'ultimo valore di confronto spinto in pila prima del endfilter comando determinerà il successo / fallimento del filtro (Catch 1  XOR  Catch 2 eseguirà di conseguenza):

Exception Filters MSIL

Inoltre, nello specifico Guid ha il Guid.TryParse metodo.


26
2017-10-07 17:31



La risposta accettata sembra accettabile, tranne che CodeAnalysis /FxCop si lamenterà del fatto che sta rilevando un tipo di eccezione generale.

Inoltre, sembra che l'operatore "è" potrebbe degradare leggermente le prestazioni.

CA1800: non eseguire il cast non necessario dice "considera invece il risultato del comando 'as' come operatore", ma se lo fai, scriverai più codice di se rilevi separatamente ciascuna eccezione.

Comunque, ecco cosa farei:

bool exThrown = false;

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

if (exThrown)
{
    // Something else
}

19
2017-07-30 17:09



Questa è una variante della risposta di Matt (sento che questo è un po 'più pulito) ... usa un metodo:

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

    WebId = Guid.Empty;
}

Qualsiasi altra eccezione verrà generata e il codice WebId = Guid.Empty; non sarà colpito. Se non vuoi che altre eccezioni blocchino il tuo programma, basta aggiungere questo DOPO le altre due prese:

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