Attraper plusieurs exceptions à la fois?

il est déconseillé de simplement attraper System.Exception . Au lieu de cela, seules les exceptions "connues" devraient être prises.

maintenant, cela conduit parfois à un code répétitif inutile, par exemple:

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

je me demande: y a-t-il un moyen d'attraper les deux exceptions et de n'appeler le WebId = Guid.Empty qu'une seule fois?

l'exemple donné est assez simple, GUID . Mais imaginez le code où vous modifiez un objet plusieurs fois, et si l'une des manipulations échoue d'une manière attendue, vous voulez "réinitialiser" le object . Cependant, s'il y a une exception inattendue, je veux quand même la jeter plus haut.

1762
demandé sur Mark Amery 2008-09-26 00:56:08

26 réponses

Attraper System.Exception et passer sur les types

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

    throw;
}
1825
répondu Joseph Daigle 2016-11-26 01:53:35

EDIT: je suis d'accord avec d'autres qui disent que, à partir de C# 6.0, les filtres d'exception sont maintenant une voie parfaitement fine pour aller: catch (Exception ex) when (ex is ... || ex is ... )

sauf que je déteste toujours la mise en page à une seule ligne et que je voudrais personnellement présenter le code comme suit. Je pense que c'est aussi fonctionnelle qu'esthétique, car je crois qu'il améliore la compréhension. Certains peuvent ne pas être d'accord:

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

ORIGINAL:

je sais que je suis un peu en retard à la fête ici, mais bon sang...

couper directement à la chasse, ce genre de duplicata une réponse précédente, mais si vous voulez vraiment effectuer une action commune pour plusieurs types d'exception et garder l'ensemble soigné et ordonné dans le cadre de la seule méthode, pourquoi ne pas simplement utiliser une fonction lambda/closure/inline pour faire quelque chose comme ce qui suit? Je veux dire, les chances sont assez bonnes que vous finirez par réaliser que vous voulez juste faire que de la fermeture d'une méthode distincte que vous pouvez utiliser partout. Mais alors, il sera très facile de le faire sans changer le reste du code structurellement. Droit?

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

je ne peux pas m'empêcher de me demander ( avertissement: un peu d'ironie/sarcasme à l'avance) pourquoi aller à tous ces efforts tout simplement de remplacer le texte suivant:

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

...avec une variation folle de cette odeur de code suivant, je veux dire un exemple, seulement pour prétendre que vous sauvez quelques frappes.

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

Parce qu'il n'est certainement pas automatiquement plus lisible.

accordé, j'ai laissé les trois cas identiques de /* write to a log, whatever... */ return; hors du premier exemple.

mais c'est un peu mon point de vue. Vous avez entendu parler des fonctions / méthodes, non? Sérieusement. Écrire une fonction commune ErrorHandler et, comme, l'appeler de chaque bloc de capture.

si vous me demandez, le deuxième exemple (avec les mots clés if et is ) est à la fois beaucoup moins lisible, et simultanément beaucoup plus sujet aux erreurs pendant la phase de maintenance de votre projet.

la phase de maintenance, pour toute personne qui pourrait être relativement nouvelle à la programmation, va comprendre 98,7% ou plus de la durée de vie globale de votre projet, et le pauvre schmuck faire le l'entretien est presque certainement va être quelqu'un d'autre que vous. Et il y a de fortes chances qu'ils passent 50% de leur temps à maudire votre nom.

et bien sûr FxCop aboie sur vous et donc vous devez aussi ajouter un attribut à votre code qui a précisément zip à voir avec le programme en cours d'exécution, et est seulement là pour dire FxCop d'ignorer un problème que dans 99,9% des cas, il est totalement correct dans de repérage. Et, désolé, je me trompe peut-être, mais cet attribut "ignore" ne finit-il pas par être compilé dans votre application?

est-ce que mettre l'ensemble du test if sur une seule ligne le rendrait plus lisible? Je ne le pense pas. Je veux dire, j'ai fait dire à un autre programmeur il y a longtemps que mettre plus de code sur une ligne le ferait "courir plus vite."Mais bien sûr, il était complétement noix. Essayer de lui expliquer (avec un visage droit-ce qui était difficile) comment le l'interprète ou le compilateur casserait cette longue ligne en énoncés discrets d'une seule instruction par ligne--essentiellement identiques au résultat s'il était allé de l'avant et avait juste rendu le code lisible au lieu d'essayer de surpasser le compilateur--n'avait aucun effet sur lui. Mais je m'égare.

Combien moins lisibles cela est-il lorsque vous ajoutez plus de trois types d'exception, un mois ou deux à partir de maintenant? (Réponse: il obtient un beaucoup moins lisible).

un des points majeurs, vraiment, est que la plupart du point de formater le code source textuel que nous regardons tous tous les jours est de le rendre vraiment, vraiment évident aux autres êtres humains ce qui se passe réellement lorsque le code court. Parce que le compilateur transforme le code source en quelque chose de totalement différent et ne pourrait pas se soucier moins de votre style de formatage de code. Donc tous sur une ligne totalement merdique, trop.

je dis juste...

// super sucks...
catch( Exception ex )
{
    if ( ex is FormatException || ex is OverflowException || ex is ArgumentNullException )
    {
        // write to a log, whatever...
        return;
    }
    throw;
}
392
répondu Craig 2017-02-14 20:38:54

comme d'autres l'ont souligné, vous pouvez avoir une déclaration if à l'intérieur de votre bloc de capture pour déterminer ce qui se passe. C#6 supporte les filtres D'Exception, de sorte que le suivant fonctionnera:

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

la méthode MyFilter pourrait alors ressembler à quelque chose comme ceci:

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

Sinon, cela peut être fait en ligne (le côté droit de la déclaration doit être une expression booléenne).

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

ceci est différent de l'utilisation d'une déclaration if dans le bloc catch , en utilisant des filtres d'exception ne sera pas décompresser la pile.

vous pouvez télécharger Visual Studio 2015 pour vérifier cela.

si vous souhaitez continuer à utiliser Visual Studio 2013, vous pouvez installer le paquet nuget suivant:

Install-Package Microsoft.Net.Compilateurs 151980920"

au moment de la rédaction du présent document, cela comprendra le soutien pour C# 6.

référencement de ce paquet fera que le projet sera construit en utilisant le version spécifique des compilateurs C# et Visual Basic contenus dans le paquet, par opposition à n'importe quelle version du système installé.

258
répondu Joe 2015-10-06 18:11:12

pas en C# malheureusement, comme vous auriez besoin d'un filtre d'exception pour le faire et C# n'expose pas cette caractéristique de MSIL. VB.NET a cette capacité cependant, par exemple

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

Ce que vous pourriez faire est d'utiliser une fonction anonyme pour encapsuler votre code d'erreur, puis l'appeler dans les blocs catch:

Action onError = () => WebId = Guid.Empty;
try
{
    // something
}
catch (FormatException)
{
    onError();
}
catch (OverflowException)
{
    onError();
}
183
répondu Greg Beech 2008-09-25 21:03:57

par souci d'exhaustivité, depuis .NET 4.0 le code peut être réécrit comme:

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

TryParse jamais lève des exceptions et retourne false si le format est incorrect, réglage WebId Guid.Empty .


depuis C# 7 vous pouvez éviter d'introduire une variable sur une ligne séparée:

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

vous pouvez aussi créer méthodes d'analyse des tuples de retour, qui ne sont pas encore disponibles dans le Framework .NET à la version 4.6:

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

et les utiliser comme ceci:

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

Suivant inutile de mise à jour de cette inutile réponse vient lors de la déconstruction des paramètres est implémenté en C# 12. :)

124
répondu Athari 2017-03-18 12:40:01

si vous pouvez mettre à jour votre application en C# 6 Vous êtes chanceux. La nouvelle version C# a implémenté des filtres D'Exception. Donc vous pouvez écrire ceci:

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

certains pensent que ce code est le même que

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

mais ce n'est pas le cas. En fait c'est la seule nouvelle fonctionnalité de C# 6 qui n'est pas possible d'émuler dans les versions antérieures. Tout d'abord, une relance signifie plus de frais généraux que de sauter la prise. Deuxièmement, elle n'est pas sémantiquement équivalente. Le nouvelle fonction préserve de la pile intacte lorsque vous déboguez votre code. Sans cette fonctionnalité, le crash dump est moins utile, voire inutile.

Voir un discussion à ce sujet sur CodePlex . Et un exemple montrant la différence .

64
répondu Maniero 2015-04-01 16:04:37

si vous ne voulez pas utiliser une déclaration if dans le catch scopes, dans C# 6.0 vous pouvez utiliser Exception Filters syntaxe qui était déjà pris en charge par le CLR dans les versions précédentes mais qui n'existait qu'en VB.NET / MSIL :

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

ce code n'attrapera le Exception que s'il s'agit d'un InvalidDataException ou ArgumentNullException .

en fait, vous pouvez mettez essentiellement n'importe quelle condition à l'intérieur de cette when clause:

static int a = 8;

...

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

notez que par opposition à une déclaration if à l'intérieur de la portée de catch , Exception Filters ne peut pas jeter Exceptions , et quand ils le font, ou quand la condition n'est pas true , la condition suivante catch sera évaluée à la place:

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

Sortie: Prise générale.

quand il est plus d'un true Exception Filter - le premier sera accepté:

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

Sortie: Catch.

et comme vous pouvez le voir dans le MSIL le code n'est pas traduit en if déclarations, mais à Filters , et Exceptions ne peut pas être jeté de l'intérieur des zones marquées avec Filter 1 et Filter 2 mais le filtre lançant le Exception va échouer à la place, également le dernier la valeur de comparaison poussée à la pile avant la commande endfilter déterminera le succès / l'échec du filtre ( Catch 1 XOR Catch 2 s'exécutera en conséquence):

Exception Filters MSIL

aussi, spécifiquement Guid a la Guid.TryParse méthode.

28
répondu Tamir Vered 2015-10-07 18:37:42

la réponse acceptée semble acceptable, sauf que CodeAnalysis/ FxCop se plaindra du fait qu'il est d'attraper un type d'exception générale.

de plus, il semble que l'opérateur" is " puisse dégrader légèrement les performances.

CA1800: ne pas lancer inutilement "dit de" envisager de tester le résultat de l'opérateur " as "à la place", mais si vous faites cela, vous écrirez plus de code que si vous attrapez chaque exception séparément.

de toute façon, voilà ce que je ferais:

bool exThrown = false;

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

if (exThrown)
{
    // Something else
}
19
répondu Matt 2015-04-09 14:09:55

c'est une variante de la réponse de Matt (je pense que c'est un peu plus propre)...utiliser une méthode:

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

    WebId = Guid.Empty;
}

toutes les autres exceptions seront lancées et le code WebId = Guid.Empty; ne sera pas frappé. Si vous ne voulez pas que d'autres exceptions ruinent votre programme, ajoutez simplement ceci après les deux autres captures:

...
catch (Exception)
{
     // something, if anything
     return; // only need this if you follow the example I gave and put it all in a method
}
18
répondu bsara 2013-10-11 20:52:50

@Micheal

version légèrement révisée de votre code:

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

les comparaisons de chaîne sont laides et lentes.

17
répondu FlySwat 2008-09-25 21:16:23

dans C# 6 l'approche recommandée est d'utiliser des filtres D'Exception, voici un exemple:

 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
répondu SHM 2015-10-07 07:53:58

la réponse de Joseph Daigle est une bonne solution, mais j'ai trouvé la structure suivante pour être un peu plus rangé et moins sujet aux erreurs.

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

    // Handle exception
}

il y a quelques avantages à inverser l'expression:

  • Une instruction return n'est pas nécessaire
  • le code n'est pas imbriqué
  • il n'y a aucun risque d'oublier les déclarations de " jet " ou "retour" que dans solution sont séparées de l'expression.

Il peut même être compactée à une seule ligne (pas très jolie)

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

    // Handle exception
}

modifier: Le filtrage d'exception dans C# 6.0 rendra la syntaxe un peu plus propre et est livré avec un nombre d'autres avantages sur n'importe quelle solution actuelle. (notamment en laissant la pile sains et saufs)

voici à quoi ressemblerait le même problème en utilisant la syntaxe C# 6.0:

catch(Exception ex) when (ex is SomeException || ex is OtherException)
{
    // Handle exception
}
16
répondu Stefan T 2017-05-23 10:31:35

Que Diriez-vous de

try
{
    WebId = Guid.Empty;
    WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
}
catch (OverflowException)
{
}
13
répondu Maurice 2013-03-22 15:58:32

Avec C# 7 la réponse de Michael Stum peut être améliorée tout en conservant la lisibilité d'une instruction switch:

catch (Exception ex)
{
    switch (ex)
    {
        case FormatException _:
        case OverflowException _:
            WebId = Guid.Empty;
            break;
        default:
            throw;
    }
}
13
répondu Fabian 2018-01-05 11:43:42
Les filtres D'Exception

sont maintenant disponibles en c# 6+. Vous pouvez faire

try
{
       WebId = new Guid(queryString["web"]);
}
catch (Exception ex) when(ex is FormatException || ex is OverflowException)
{
     WebId = Guid.Empty;
}
13
répondu Mat J 2018-07-11 13:12:05

avertit et avertit: encore un autre type, le style fonctionnel.

ce qui est dans le lien ne répond pas directement à votre question, mais il est trivial de l'étendre pour ressembler à:

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

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

(essentiellement fournir une autre surcharge vide Catch qui retourne elle-même)

la grande question Est pourquoi . Je ne pense pas que le coût l'emporte le gain ici :)

12
répondu nawfal 2013-05-18 11:28:30
catch (Exception ex)
{
    if (!(
        ex is FormatException ||
        ex is OverflowException))
    {
        throw;
    }
    Console.WriteLine("Hello");
}
12
répondu Konstantin Spirin 2014-10-30 11:15:16

mise à jour 2015-12-15: voir https://stackoverflow.com/a/22864936/1718702 pour C#6. C'est un nettoyeur et maintenant standard dans le langage.

conçu pour les gens qui veulent une solution plus élégante pour attraper une fois et filtrer les exceptions, j'utilise une méthode d'extension comme démontré ci-dessous.

j'ai déjà eu cette extension dans ma bibliothèque, à l'origine écrite pour d'autres fins, mais il a fonctionné juste parfaitement pour type vérifier les exceptions. En plus, imho, ça a l'air plus propre qu'un tas de déclarations || . De plus, contrairement à la réponse acceptée, je préfère le traitement d'exception explicite de sorte que ex is ... a eu un comportement indésirable car les classes dérivées sont assignables aux types de parents).

Utilisation

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

IsAnyOf.cs Extension (voir Exemple de gestion complète des erreurs pour les dépendances))

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

Plein d'Erreur de Manipulation de l'Exemple (Copier-Coller à la nouvelle application 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));
        }
    }
}

Deux Essais À L'Unité NUnit

Correspondant à des comportements Exception types est exact (ie. Un enfant ne correspond à aucun de ses parents).

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*/
            }
        }
    }
}
10
répondu HodlDwon 2017-05-23 10:31:35

comme j'avais l'impression que ces réponses avaient touché la surface, j'ai essayé de creuser un peu plus.

donc ce que nous voulons vraiment faire c'est quelque chose qui ne compile pas, disons:

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

la raison pour laquelle nous voulons cela est que nous ne voulons pas que le gestionnaire d'exception attrape des choses dont nous avons besoin plus tard dans le processus. Bien sûr, nous pouvons attraper une Exception et vérifier avec un " si " Que faire, mais soyons honnêtes, nous ne voulons pas vraiment cela. (FxCop, problèmes de débogage, uglyness)

alors pourquoi ce code ne sera - t-il pas compilé-et comment pouvons-nous le hacker de telle manière qu'il le sera?

Si l'on regarde le code, ce que nous aimerions vraiment faire est de transférer l'appel. Cependant, selon la Partition MS II, les blocs de handler d'exception IL ne fonctionneront pas comme ceci, ce qui dans ce cas est logique car cela impliquerait que l'objet "exception" peut avoir différents types.

ou pour l'écrire en code, nous demandons au compilateur de faire quelque chose comme ça (ce n'est pas tout à fait correct, mais c'est la chose la plus proche possible que j'imagine):

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

la raison pour laquelle cela ne compile pas est évidente: quel type et quelle valeur aurait l'objet '$exception' (qui sont ici stockés dans les variables 'e')? La façon dont nous voulons que le compilateur gère ceci est de noter que le type de base commun des deux exceptions est 'Exception', utilisez cela pour qu'une variable contienne les deux exceptions, et puis gérer seulement les deux exceptions qui sont attrapées. La façon dont cela est mis en œuvre dans IL est comme "filtre", qui est disponible en VB.Net.

pour que cela fonctionne dans C#, nous avons besoin d'une variable temporaire avec le type de base 'Exception' correct. Pour contrôler le flux de code, nous pouvons ajouter quelques branches.

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

les inconvénients évidents pour cela sont que nous ne pouvons pas relancer correctement, et - soyons honnêtes-que c'est tout à fait la mauvaise solution. Le uglyness peut être fixé un peu en effectuant l'élimination de la branche, ce qui rend la solution légèrement meilleure:

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

il ne reste que le "lancer de nouveau". Pour que cela fonctionne, nous devons être capables d'effectuer la manipulation à l'intérieur du bloc 'catch' - et la seule façon de faire ce travail est par un objet Catch 'Exception'.

à ce point, nous pouvons ajouter une fonction séparée qui traite les différents types d'Exceptions en utilisant la résolution de surcharge, ou à gérer l'Exception. Les deux ont des inconvénients. Pour commencer, voici comment le faire avec une fonction helper:

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

et l'autre solution est d'attraper l'objet D'Exception et de le manipuler en conséquence. La traduction la plus littérale pour cela, basée sur le contexte ci-dessus est celle-ci:

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

pour conclure:

  • si nous ne voulons pas jeter de nouveau, nous pourrions envisager d'attraper les bonnes exceptions, et de stocker dans un temporaire.
  • si le gestionnaire est simple, et que nous voulons réutiliser le code, la meilleure solution est probablement d'introduire une fonction d'aide.
  • si nous voulons lancer à nouveau, nous n'avons pas d'autre choix que de mettre le code dans un gestionnaire de captures "Exception", qui cassera FxCop et les exceptions non récupérées de votre débogueur.
7
répondu atlaste 2014-10-21 09:44:02

donc vous répétez beaucoup de code dans chaque exception-switch? On dirait qu'extraire une méthode serait une idée de Dieu, n'est-ce pas?

donc votre code se résume à ceci:

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

je me demande pourquoi personne n'a remarqué cette duplication de code.

de C#6 Vous avez en outre le exception-filtres comme déjà mentionné par d'autres. Vous pouvez donc modifier le code ci-dessus comme suit:

try { ... }
catch(Exception e) when(e is Exception1 || e is Exception2)
{ 
    Reset(instance); 
}
6
répondu HimBromBeere 2017-05-23 11:47:26

C'est un problème classique auquel tout développeur C# est éventuellement confronté.

permettez-moi de diviser votre question en deux questions. Le premier,

puis-je attraper plusieurs exceptions à la fois?

en bref, non.

qui conduit à la question suivante,

Comment éviter d'écrire un code double étant donné que je ne peux pas attraper plusieurs types d'exception dans le même catch() bloc?

étant donné votre échantillon spécifique, où la valeur de repli est peu coûteuse à construire, j'aime suivre ces étapes:

  1. initialiser WebId à la valeur de repli.
  2. construisez une nouvelle interface dans une variable temporaire.
  3. définit WebId à la variable temporaire entièrement construite. C'est la déclaration finale du bloc d'essai.

So le code ressemble à:

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

si N'importe quelle exception est lancée, alors WebId N'est jamais mis à la valeur demi-construite, et reste Guid.Vide.

si construire la valeur de repli est coûteux, et réinitialiser une valeur est beaucoup moins cher, alors je déplacerais le code de réinitialisation dans sa propre fonction:

try
{
    WebId = new Guid(queryString["web"]);
    // More initialization code goes here.
}
catch (FormatException) {
    Reset(WebId);
}
catch (OverflowException) {
    Reset(WebId);
}
5
répondu Jeffrey Rennie 2017-11-15 20:27:32

notez que j'ai trouvé une façon de le faire, mais cela ressemble plus à du matériel pour le quotidien WTF :

catch (Exception ex)
{
    switch (ex.GetType().Name)
    {
        case "System.FormatException":
        case "System.OverflowException":
            WebId = Guid.Empty;
            break;
        default:
            throw;
    }
}
3
répondu Michael Stum 2015-04-09 14:07:34

a voulu ajouter ma réponse courte à ce déjà long fil. Quelque chose qui n'a pas été mentionné, c'est l'ordre de priorité des instructions catch, plus spécifiquement, vous avez besoin d'être conscient de la portée de chaque type d'exception que vous essayez d'attraper.

par exemple si vous utilisez une exception" catch-all "comme Exception il préceed toutes les autres déclarations de capture et vous obtiendrez évidemment des erreurs de compilateur cependant si vous inversez la ordre vous pouvez enchaîner vos déclarations de capture (un peu d'un anti-modèle je pense) vous pouvez mettre le catch-all Exception type au bas et ce sera capturer toutes les exceptions qui ne répondaient pas à plus haut dans votre essai..bloc 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);
            }

je recommande fortement les gens de l'examen de ce document MSDN:

L'Exception " Hiérarchie

3
répondu Tahir Khalid 2017-04-16 15:51:04

peut-être essayer de garder votre code simple comme mettre le code commun dans une méthode, comme vous le feriez dans n'importe quelle autre partie du code qui n'est pas dans une clause catch?

par exemple:

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

// ...

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

comme je le ferais, en essayant de trouver le simple is beautiful pattern

2
répondu Żubrówka 2018-01-23 14:02:59

il suffit d'appeler le try and catch deux fois.

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

c'est aussi Simple que ça!!

-13
répondu scholar guy 2016-07-29 04:42:02

En c# 6.0,Filtres d'Exception est l'amélioration de la gestion des exceptions

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");
    }
}
-20
répondu Kashif 2015-05-20 07:48:59