c# - try - vb net exception handling best practices




Cattura più eccezioni contemporaneamente? (18)

È scoraggiato catturare semplicemente 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: esiste un modo per intercettare entrambe le eccezioni e chiamare la chiamata WebId = Guid.Empty 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 modo prevedibile, vuoi "resettare" l' object . Tuttavia, se c'è un'eccezione imprevista, voglio comunque lanciarla più in alto.


Attenzione e avvertenze: ancora un altro tipo, stile funzionale.

Quello che c'è nel link non risponde direttamente alla tua domanda, ma è banale estenderlo a qualcosa di simile:

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

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

(Fornisci fondamentalmente un altro overload Catch vuoto che restituisce sé stesso)

La domanda più grande per questo è perché . Non penso che il costo superi il guadagno qui :)


@Micheal

Versione leggermente rivista del tuo codice:

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

I confronti tra stringhe sono brutti e lenti.


Che ne dite di

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

Come altri hanno sottolineato, puoi avere un'istruzione if nel 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 metodo MyFilter potrebbe quindi somigliare 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'uso di un'istruzione if all'interno del blocco catch , utilizzando i filtri delle eccezioni non si srotolerà lo stack.

È possibile 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 la versione specifica dei compilatori C # e Visual Basic contenuti nel pacchetto, a differenza di qualsiasi versione installata del sistema.


I filtri di eccezione sono ora disponibili in C # 6+. Tu puoi fare

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

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

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

CA1800: non inoltrare inutilmente dice di "considerare invece di testare il risultato dell'operatore 'as'", 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
}

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

Eventuali altre eccezioni verranno generate 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
}

Se non si desidera utilizzare un'istruzione if negli ambiti catch , in C# 6.0 è possibile utilizzare la sintassi dei Exception Filters che era già supportata dal CLR nelle versioni di 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 rileverà l' Exception solo quando si tratta di una InvalidDataException o ArgumentNullException .

In realtà, puoi mettere praticamente tutte le condizioni 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'istruzione if all'interno dell'ambito catch , i Exception Filters non possono generare Exceptions e, quando lo fanno, o quando la condizione non è true , la successiva condizione di catch verrà valutata:

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 c'è più di un 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 viene tradotto in istruzioni, ma in Filters , e le Exceptions non possono essere lanciate dall'interno delle aree contrassegnate con Filter 1 e Filter 2 ma il filtro che lancia l' Exception fallirà invece, anche l'ultimo valore di confronto endfilter allo stack prima che il comando endfilter determini il successo / l'errore del filtro ( Catch 1 XOR Catch 2 verrà eseguito di conseguenza):

Inoltre, in particolare, Guid ha il metodo Guid.TryParse .


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

in C # 6 l'approccio consigliato è usare i filtri di eccezione, ecco un esempio:

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

EDIT: Concordo con gli altri che stanno dicendo che, a partire da C # 6.0, i filtri delle eccezioni ora sono 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 ( attenzione: un po 'di ironia / sarcasmo in anticipo) 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.

Certo, ho lasciato le tre istanze identiche 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 una funzione ErrorHandler comune e, come, chiamala da ogni blocco catch.

Se me lo chiedi, il secondo esempio (con le parole chiave if e is ) è significativamente meno leggibile e contemporaneamente significativamente più soggetto a errori durante la fase di manutenzione del tuo 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 anche aggiungere 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 è totalmente correggere in flagging. E, scusa, potrei sbagliarmi, ma questo attributo "ignora" non è compilato nella tua app?

Mettere l'intero if 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 è meno leggibile ciò quando si aggiungono altri tre tipi di eccezioni, un mese o due da adesso? (Risposta: diventa molto 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;
}

Basta chiamare e provare due volte.

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

È proprio così semplice !!


In c # 6.0, i filtri delle eccezioni sono miglioramenti per la gestione delle eccezioni

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

Poiché mi sono sentito come se queste risposte toccassero appena la superficie, ho tentato di scavare un po 'più a fondo.

Quindi quello che vorremmo davvero fare è qualcosa che non si compila, diciamo:

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

Il motivo per cui lo vogliamo è che non vogliamo che il gestore delle eccezioni catturi le cose di cui abbiamo bisogno in seguito nel processo. Certo, possiamo prendere un'eccezione e verificare con un "se" cosa fare, ma siamo onesti, non lo vogliamo davvero. (FxCop, problemi di debugger, brutto)

Quindi, perché questo codice non verrà compilato e come possiamo modificarlo in modo tale che lo farà?

Se guardiamo il codice, quello che vorremmo davvero fare è inoltrare la chiamata. Tuttavia, secondo MS Partition II, i blocchi del gestore delle eccezioni IL non funzioneranno in questo modo, il che in questo caso ha senso perché ciò implicherebbe che l'oggetto 'eccezione' possa avere tipi diversi.

O per scriverlo in codice, chiediamo al compilatore di fare qualcosa del genere (beh, non è del tutto corretto, ma è la cosa più vicina possibile, immagino):

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

La ragione per cui questo non verrà compilato è abbastanza ovvia: quale tipo e valore avrebbe l'oggetto '$ exception' (che sono qui memorizzati nelle variabili 'e')? Il modo in cui vogliamo che il compilatore gestisca ciò è notare che il tipo di base comune di entrambe le eccezioni è 'Exception', utilizzare quello per una variabile per contenere entrambe le eccezioni e quindi gestire solo le due eccezioni catturate. Il modo in cui questo è implementato in IL è come 'filtro', che è disponibile in VB.Net.

Per farlo funzionare in C #, abbiamo bisogno di una variabile temporanea con il corretto tipo di base 'Eccezione'. Per controllare il flusso del codice, possiamo aggiungere alcuni rami. Ecco qui:

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

Gli ovvi svantaggi di questo sono che non possiamo ri-gettare correttamente, e, be ', siamo onesti, è una soluzione abbastanza brutta. La bruttura può essere risolta un po 'eseguendo l'eliminazione dei rami, il che rende la soluzione leggermente migliore:

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

Ciò lascia solo il "ri-lancio". Affinché funzioni, dobbiamo essere in grado di eseguire la gestione all'interno del blocco 'catch', e l'unico modo per farlo è quello di catturare un oggetto 'Exception'.

A questo punto, possiamo aggiungere una funzione separata che gestisce i diversi tipi di Eccezioni usando la risoluzione di sovraccarico o per gestire l'Eccezione. Entrambi hanno degli svantaggi. Per iniziare, ecco il modo di farlo con una funzione di aiuto:

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

E l'altra soluzione è catturare l'oggetto Exception e gestirlo di conseguenza. La traduzione più letterale per questo, basata sul contesto sopra riportato è questa:

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

Quindi per concludere:

  • Se non vogliamo rilanciare, potremmo considerare di prendere le giuste eccezioni e conservarle temporaneamente.
  • Se il gestore è semplice e vogliamo riutilizzare il codice, la soluzione migliore è probabilmente quella di introdurre una funzione di supporto.
  • Se vogliamo rilanciare, non abbiamo altra scelta che inserire il codice in un gestore catch 'Exception', che interromperà FxCop e le eccezioni non rilevate del debugger.

Aggiornamento 2015-12-15: vedere https://.com/a/22864936/1718702 per C # 6. È un detergente e ora standard nella lingua.

Pensato per le persone che desiderano catturare una soluzione più elegante e filtrare le eccezioni, utilizzo un metodo di estensione come illustrato di seguito.

Ho già questa estensione nella mia libreria, originariamente scritta per altri scopi, ma ha funzionato perfettamente per il typecontrollo delle eccezioni. Inoltre, imho, sembra più pulito di un sacco di ||dichiarazioni. Inoltre, a differenza della risposta accettata, preferisco la gestione esplicita delle eccezioni, quindi ex is ...avevo un comportamento indesiderato in quanto le classi derrivate sono assegnabili a quei tipi di genitori).

uso

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

Estensione IsAnyOf.cs (vedi Esempio di gestione errori completa per dipendenze)

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

Esempio di gestione completa degli errori (copia-incolla nella nuova app della console)

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

Due test di unità campione NUnit

Il comportamento di corrispondenza per i Exceptiontipi è esatto (cioè un bambino NON è una corrispondenza per nessuno dei suoi tipi principali).

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

Nota che ho trovato un modo per farlo, ma questo sembra più materiale per The Daily WTF :

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

Volevo aggiungere la mia risposta breve a questo thread già lungo. Qualcosa che non è stato menzionato è l'ordine di precedenza delle dichiarazioni catch, in particolare è necessario essere consapevoli della portata di ogni tipo di eccezione che si sta cercando di catturare.

Ad esempio, se si utilizza un'eccezione "catch-all" come Exception , verranno precedute tutte le altre dichiarazioni catch e ovviamente si otterranno errori del compilatore, tuttavia se si inverte l'ordine è possibile concatenare le proprie dichiarazioni catch (un po 'di anti-pattern credo ) puoi mettere il tipo di eccezioni catch-all in basso e questo catturerà tutte le eccezioni che non sono adatte per l'alto nel tuo blocco try..catch:

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

Consiglio vivamente la gente rivedere questo documento MSDN:

Gerarchia delle eccezioni


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




exception-handling