Catch multiples exceptions à la fois?

Il est déconseillé d’attraper simplement System.Exception . Au lieu de cela, seules les exceptions “connues” devraient être sockets en compte.

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

 try { WebId = new Guid(querySsortingng["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 d’appeler seulement l’appel WebId = Guid.Empty une fois?

L’exemple donné est plutôt simple, car ce n’est qu’un GUID . Mais imaginez du code où vous modifiez plusieurs fois un object, et si l’une des manipulations échoue de manière attendue, vous souhaitez “réinitialiser” l’ object . Cependant, s’il y a une exception inattendue, je veux quand même le faire passer plus haut.

Attraper System.Exception et activer les types

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

EDIT: Je suis d’accord avec d’autres qui disent que, à partir de C # 6.0, les filtres d’exception sont désormais un moyen parfait: catch (Exception ex) when (ex is ... || ex is ... )

Sauf que je déteste toujours la mise en page longue ligne et que je dépose personnellement le code comme suit: Je pense que c’est aussi fonctionnel qu’esthétique, car je pense que cela améliore la compréhension. Certains peuvent être en désaccord:

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

ORIGINAL:

Je sais que je suis un peu en retard pour la fête ici, mais la fumée sacrée …

En allant droit au but, ce type de doublon correspond à une réponse antérieure, mais si vous voulez vraiment effectuer une action commune pour plusieurs types d’exceptions et conserver le tout dans le cadre de la méthode, pourquoi ne pas utiliser un lambda? Fonction / closed / inline pour faire quelque chose comme ceci? Je veux dire, les chances sont plutôt bonnes que vous finissiez par réaliser que vous voulez faire de cette fermeture une méthode distincte que vous pouvez utiliser partout. Mais il sera alors très facile de le faire sans changer structurellement le rest du code. Droite?

 private void TestMethod () { Action 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 ( mise en garde: un peu d’ironie / sarcasme devant nous) pourquoi, sur terre, faire tout ce travail pour simplement remplacer ce qui suit:

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

… avec quelques variations folles de cette nouvelle odeur de code, je veux dire par exemple pour prétendre que vous enregistrez quelques touches.

 // 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 que ce n’est certainement pas automatiquement plus lisible.

Certes, j’ai laissé les trois instances identiques de /* write to a log, whatever... */ return; du premier exemple.

Mais c’est en quelque sorte mon point. Vous avez entendu parler de fonctions / méthodes, non? Sérieusement. Ecrivez une fonction ErrorHandler commune et appelez-la depuis chaque bloc catch.

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

La phase de maintenance, pour quiconque pourrait être relativement nouveau en programmation, représentera 98,7% ou plus de la durée de vie totale de votre projet, et le pauvre schmuck effectuant la maintenance sera certainement quelqu’un d’autre que vous. Et il y a de fortes chances qu’ils consacrent 50% de leur temps au travail en maudissant votre nom.

Et bien sûr, FxCop aboie contre vous et vous devez donc append un atsortingbut à votre code qui a précisément une fonction zip à faire avec le programme en cours d’exécution. Il est seulement là pour dire à FxCop d’ignorer un problème qui dans 99,9% des cas correct dans le marquage. Et, désolé, je pourrais me tromper, mais cet atsortingbut “ignorer” ne se retrouve-t-il pas réellement compilé dans votre application?

Est-ce que mettre le test complet sur une seule ligne le rendrait plus lisible? Je ne pense pas. Je veux dire, j’ai eu un autre programmeur argumenter avec véhémence il y a longtemps que mettre plus de code sur une seule ligne le ferait “courir plus vite”. Mais bien sûr, il était complètement fou. Tenter de lui expliquer (avec un visage franc – ce qui était difficile) comment l’interpréteur ou le compilateur séparerait cette longue ligne en instructions discrètes à une instruction par ligne – essentiellement identique au résultat s’il était allé de l’avant et juste rendu le code lisible au lieu d’essayer de surpasser le compilateur – n’a eu aucun effet sur lui que ce soit. Mais je m’égare.

À quel point cela est-il moins lisible lorsque vous ajoutez trois types d’exceptions supplémentaires, un mois ou deux à partir de maintenant? (Réponse: ça devient beaucoup moins lisible).

Un des points majeurs, en réalité, est que la plupart du temps, le formatage du code source textuel que nous examinons tous les jours, est de le rendre vraiment évident pour les autres êtres humains. Parce que le compilateur transforme le code source en quelque chose de totalement différent et se moque de votre style de formatage de code. Donc, tout-en-un-ligne aspire totalement aussi.

Juste dire …

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

Comme d’autres l’ont fait remarquer, vous pouvez avoir une instruction if dans votre bloc catch pour déterminer ce qui se passe. C # 6 prend en charge les filtres d’exception. Les éléments suivants fonctionnent:

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

La méthode MyFilter pourrait alors ressembler à ceci:

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

Sinon, tout cela peut être fait en ligne (le côté droit de l’instruction when doit simplement être une expression booléenne).

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

Cela diffère de l’utilisation d’une instruction if depuis le bloc catch , l’utilisation des filtres d’exception ne permet pas de dérouler la stack.

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

Si vous souhaitez continuer à utiliser Visual Studio 2013, vous pouvez installer le package nuget suivant:

Package d’installation Microsoft.Net.Comstackrs

Au moment de la rédaction, ceci inclura le support pour C # 6.

Le référencement de ce package entraînera la construction du projet en utilisant la version spécifique des compilateurs C # et Visual Basic contenus dans le package, par opposition à toute version installée sur le système.

Pas en C # malheureusement, car vous auriez besoin d’un filtre d’exception pour le faire et C # n’expose pas cette fonctionnalité de MSIL. VB.NET a cependant cette capacité, par exemple

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

Ce que vous pouvez faire, c’est utiliser une fonction anonyme pour encapsuler votre code d’erreur, puis l’appeler dans ces blocs catch spécifiques:

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

Par souci d’exhaustivité, depuis .NET 4.0, le code peut être réécrit comme suit:

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

TryParse ne lève jamais les exceptions et renvoie false si le format est incorrect, définissant WebId sur Guid.Empty .


Depuis C # 7, vous pouvez éviter d’introduire une variable sur une ligne distincte:

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

Vous pouvez également créer des méthodes pour parsingr les tuples retournés, qui ne sont pas encore disponibles dans .NET Framework à partir de la version 4.6:

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

Et les utiliser comme ça:

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

La prochaine mise à jour inutile de cette réponse inutile survient lorsque la déconstruction des parameters externes est implémentée dans C # 12. 🙂

Si vous pouvez mettre à jour votre application en C # 6, vous avez de la chance. La nouvelle version de 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; } 

Certaines personnes 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. En fait, c’est la seule nouvelle fonctionnalité de C # 6 qu’il est impossible d’émuler dans les versions précédentes. Tout d’abord, un re-lancer signifie plus de temps en tête que de sauter la prise. Deuxièmement, ce n’est pas sémantiquement équivalent. La nouvelle fonctionnalité conserve la stack intacte lorsque vous déboguez votre code. Sans cette fonctionnalité, le vidage sur incident est moins utile ou même inutile.

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

Si vous ne souhaitez pas utiliser une instruction if dans les scopes catch , dans C# 6.0 vous pouvez utiliser la syntaxe des Exception Filters qui était déjà prise en charge par le CLR dans les versions d’aperçu, mais qui n’existait que dans VB.NET / MSIL :

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

Ce code intercepte l’ Exception uniquement lorsqu’il s’agit d’une InvalidDataException ou ArgumentNullException .

En fait, vous pouvez mettre pratiquement n’importe quelle condition dans cette clause when :

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

Notez que, contrairement à une instruction if dans la scope de catch , Exception Filters ne peuvent pas lancer d’ Exceptions , et lorsqu’ils le font, ou lorsque la condition n’est pas true , la condition de catch suivante 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.

Lorsqu’il y a 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 dans les instructions if , mais dans les Filters , et les Exceptions ne peuvent pas être lancées dans les zones marquées par les Filter 1 et Filter 2 mais le dernier La valeur de comparaison endfilter à la stack avant que la commande endfilter détermine le succès / l’échec du filtre ( Catch 1 XOR Catch 2 s’exécutera en conséquence):

Filtres d'exception MSIL

En outre, spécifiquement, Guid a la méthode Guid.TryParse .

La réponse acceptée semble acceptable, sauf que CodeAnalysis / FxCop va se plaindre du fait qu’il détecte un type d’exception générale.

En outre, il semble que l’opérateur “is” puisse dégrader légèrement les performances.

CA1800: Ne lancez pas inutilement le message “envisagez plutôt de tester le résultat de l’opérateur ‘en tant qu’opérateur”, mais si vous faites cela, vous écrirez plus de code que si vous détectiez chaque exception séparément.

Quoi qu’il en soit, voici ce que je ferais:

 bool exThrown = false; try { // Something } catch (FormatException) { exThrown = true; } catch (OverflowException) { exThrown = true; } if (exThrown) { // Something else } 

Ceci est une variante de la réponse de Matt (je pense que c’est un peu plus propre) … utilisez 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 touché. Si vous ne voulez pas que d’autres exceptions bloquent 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 } 

@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înes sont laides et lentes.

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

La réponse de Joseph Daigle est une bonne solution, mais j’ai trouvé la structure suivante un peu plus nette et moins sujette aux erreurs.

 catch(Exception ex) { if (!(ex is SomeException || ex is OtherException)) throw; // Handle exception } 

Il y a quelques avantages à inverser l’expression:

  • Une déclaration de retour n’est pas nécessaire
  • Le code n’est pas nested
  • Il n’y a pas de risque d’oublier les déclarations “throw” ou “return” qui dans la solution de Joseph sont séparées de l’expression.

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

 catch(Exception ex) { if (!(ex is SomeException || ex is OtherException)) throw; // Handle exception } 

Edit: le filtrage des exceptions dans C # 6.0 rendra la syntaxe un peu plus claire et apportera un certain nombre d’autres avantages par rapport à la solution actuelle. (notamment en laissant la stack indemne)

Voici comment le même problème se pose en utilisant la syntaxe C # 6.0:

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

Que diriez-vous

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

Attention et avertissement: encore un autre type, style fonctionnel.

Ce qui est dans le lien ne répond pas directement à votre question, mais il est sortingvial de l’étendre pour ressembler à ceci:

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

(Fondamentalement fournir une autre surcharge de Catch vide qui retourne lui-même)

La plus grande question est la suivante. Je ne pense pas que le coût dépasse le gain ici 🙂

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

Mise à jour 2015-12-15: Voir https://stackoverflow.com/a/22864936/1718702 pour C # 6. C’est un nettoyant et maintenant standard dans la langue.

Conçu pour les personnes qui veulent une solution plus élégante pour capturer une fois et filtrer les exceptions, j’utilise une méthode d’extension comme indiqué ci-dessous.

J’avais déjà cette extension dans ma bibliothèque, écrite à l’origine à d’autres fins, mais elle fonctionnait parfaitement pour la vérification de type des exceptions. De plus, à mon humble avis, il semble plus propre qu’un tas de || déclarations. En outre, contrairement à la réponse acceptée, je préfère la gestion des exceptions explicites, donc ex is ... un comportement indésirable, car les classes dérivées sont assignables à ces types de parents).

Usage

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

Extension IsAnyOf.cs (voir Exemple de traitement d’erreur complet pour les dépendances)

 namespace Common.FluentValidation { public static partial class Validate { ///  /// Validates the passed in parameter matches at least one of the passed in comparisons. ///  ///  /// Parameter to validate. /// Values to compare against. /// True if a match is found. ///  public static bool IsAnyOf(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; } } } 

Full Error Handling Example (Copy-Paste to new Console app)

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using Common.FluentValidation; namespace IsAnyOfExceptionHandlerSample { class Program { static void Main(ssortingng[] 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> TestActions = new List>() { (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.ToSsortingng()); 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 { ///  /// Validates the passed in parameter matches at least one of the passed in comparisons. ///  ///  /// Parameter to validate. /// Values to compare against. /// True if a match is found. ///  public static bool IsAnyOf(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; } ///  /// Validates if any passed in parameter is equal to null. ///  /// Parameters to test for Null. /// True if one or more parameters are null. 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 { ///  /// Validates the passed in parameter is not null, throwing a detailed exception message if the test fails. ///  /// Parameter to validate. /// Name of tested parameter to assist with debugging. ///  public static void CannotBeNull(this object p_parameter, ssortingng p_name) { if (p_parameter == null) throw new ArgumentNullException( ssortingng.Format("Parameter \"{0}\" cannot be null.", p_name), default(Exception)); } } } namespace Common.FluentValidation { public static partial class Validate { ///  /// Validates the passed in parameter is not null or an empty collection, throwing a detailed exception message if the test fails. ///  ///  /// Parameter to validate. /// Name of tested parameter to assist with debugging. ///  ///  public static void CannotBeNullOrEmpty(this ICollection p_parameter, ssortingng 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)); } ///  /// Validates the passed in parameter is not null or empty, throwing a detailed exception message if the test fails. ///  /// Parameter to validate. /// Name of tested parameter to assist with debugging. ///  public static void CannotBeNullOrEmpty(this ssortingng p_parameter, ssortingng p_name) { if (ssortingng.IsNullOrEmpty(p_parameter)) throw new ArgumentException("Ssortingng cannot be null or empty.\r\nParameter_Name: " + p_name, default(Exception)); } } } 

Two Sample NUnit Unit Tests

Matching behaviour for Exception types is exact (ie. A child IS NOT a match for any of its parent types).

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

With C# 7 the answer from Michael Stum can be improved while keeping the readability of a switch statement:

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

Exception filters are now available in c# 6+. You can do

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

Since I felt like these answers just touched the surface, I attempted to dig a bit deeper.

So what we would really want to do is something that doesn’t comstack, say:

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

The reason we want this is because we don’t want the exception handler to catch things that we need later on in the process. Sure, we can catch an Exception and check with an ‘if’ what to do, but let’s be honest, we don’t really want that. (FxCop, debugger issues, uglyness)

So why won’t this code comstack – and how can we hack it in such a way that it will?

If we look at the code, what we really would like to do is forward the call. However, according to the MS Partition II, IL exception handler blocks won’t work like this, which in this case makes sense because that would imply that the ‘exception’ object can have different types.

Or to write it in code, we ask the comstackr to do something like this (well it’s not entirely correct, but it’s the closest possible thing I guess):

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

The reason that this won’t comstack is quite obvious: what type and value would the ‘$exception’ object have (which are here stored in the variables ‘e’)? The way we want the comstackr to handle this is to note that the common base type of both exceptions is ‘Exception’, use that for a variable to contain both exceptions, and then handle only the two exceptions that are caught. The way this is implemented in IL is as ‘filter’, which is available in VB.Net.

To make it work in C#, we need a temporary variable with the correct ‘Exception’ base type. To control the flow of the code, we can add some twigs. Here goes:

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

The obvious disadvantages for this are that we cannot re-throw properly, and -well let’s be honest- that it’s quite the ugly solution. The uglyness can be fixed a bit by performing branch elimination, which makes the solution slightly better:

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

That leaves just the ‘re-throw’. For this to work, we need to be able to perform the handling inside the ‘catch’ block – and the only way to make this work is by an catching ‘Exception’ object.

At this point, we can add a separate function that handles the different types of Exceptions using overload resolution, or to handle the Exception. Both have disadvantages. To start, here’s the way to do it with a helper function:

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

And the other solution is to catch the Exception object and handle it accordingly. The most literal translation for this, based on the context above is this:

 try { throw new ArgumentException(); } catch (Exception e) { Exception ex = (Exception)(e as ArgumentException) ?? (e as IndexOutOfRangeException); if (ex != null) { Console.WriteLine("Handle the exception here :-)"); // throw ? } else { throw; } } 

So to conclude:

  • If we don’t want to re-throw, we might consider catching the right exceptions, and storing them in a temporary.
  • If the handler is simple, and we want to re-use code, the best solution is probably to introduce a helper function.
  • If we want to re-throw, we have no choice but to put the code in a ‘Exception’ catch handler, which will break FxCop and your debugger’s uncaught exceptions.

So you´re repeating lots of code within every exception-switch? Sounds like extracting a method would be god idea, doesn´t it?

So your code comes down to this:

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

I wonder why no-one noticed that code-duplication.

From C#6 you furthermore have the exception-filters as already mentioned by others. So you can modify the code above to this:

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

This is a classic problem every C# developer faces eventually.

Let me break your question into 2 questions. The first,

Can I catch multiple exceptions at once?

In short, no.

Which leads to the next question,

How do I avoid writing duplicate code given that I can’t catch multiple exception types in the same catch() block?

Given your specific sample, where the fall-back value is cheap to construct, I like to follow these steps:

  1. Initialize WebId to the fall-back value.
  2. Construct a new Guid in a temporary variable.
  3. Set WebId to the fully constructed temporary variable. Make this the final statement of the try{} block.

So the code looks like:

 try { WebId = Guid.Empty; Guid newGuid = new Guid(querySsortingng["web"]); // More initialization code goes here like // newGuid.x = y; WebId = newGuid; } catch (FormatException) {} catch (OverflowException) {} 

If any exception is thrown, then WebId is never set to the half-constructed value, and remains Guid.Empty.

If constructing the fall-back value is expensive, and resetting a value is much cheaper, then I would move the reset code into its own function:

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

Note that I did find one way to do it, but this looks more like material for The Daily WTF :

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

Wanted to added my short answer to this already long thread. Something that hasn’t been mentioned is the order of precedence of the catch statements, more specifically you need to be aware of the scope of each type of exception you are trying to catch.

For example if you use a “catch-all” exception as Exception it will preceed all other catch statements and you will obviously get comstackr errors however if you reverse the order you can chain up your catch statements (bit of an anti-pattern I think) you can put the catch-all Exception type at the bottom and this will be capture any exceptions that didn’t cater for higher up in your try..catch block:

  try { // do some work here } catch (WebException ex) { // catch a web excpetion } catch (ArgumentException ex) { // do some stuff } catch (Exception ex) { // you should really surface your errors but this is for example only throw new Exception("An error occurred: " + ex.Message); } 

I highly recommend folks review this MSDN document:

Exception Hierarchy

Maybe try to keep your code simple such as putting the common code in a method, as you would do in any other part of the code that is not inside a catch clause?

Par exemple:

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

Just how I would do it, trying to find the simple is beautiful pattern

Just call the try and catch twice.

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

It is just that Simple!!

In c# 6.0,Exception Filters is improvements for exception handling

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