Capturas múltiples excepciones a la vez?

Se desaconseja capturar simplemente System.Exception . En cambio, solo las excepciones “conocidas” deben ser atrapadas.

Ahora, esto a veces conduce a un código repetitivo innecesario, por ejemplo:

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

Me pregunto: ¿hay alguna forma de detectar ambas excepciones y solo llamar a la llamada WebId = Guid.Empty una vez?

El ejemplo dado es bastante simple, ya que es solo un GUID . Pero imagine un código en el que modifique un objeto varias veces, y si una de las manipulaciones falla de manera esperada, desea “restablecer” el object . Sin embargo, si hay una excepción inesperada, aún quiero arrojar eso más alto.

Catch System.Exception y System.Exception los tipos

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

EDITAR: Estoy de acuerdo con otros que dicen que, a partir de C # 6.0, los filtros de excepción ahora son un camino perfectamente perfecto: catch (Exception ex) when (ex is ... || ex is ... )

Excepto que todavía odio el diseño de una sola línea larga y, en lo personal, tendré el código como el siguiente. Creo que esto es tan funcional como estético, ya que creo que mejora la comprensión. Algunos pueden estar en desacuerdo:

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

ORIGINAL:

Sé que llegué un poco tarde a la fiesta aquí, pero el humo sagrado …

Directamente a la persecución, este tipo de duplica una respuesta anterior, pero si realmente desea realizar una acción común para varios tipos de excepciones y mantener todo limpio y ordenado dentro del scope de un método, ¿por qué no simplemente usar un lambda? / cierre / función en línea para hacer algo como lo siguiente? Quiero decir, las posibilidades son bastante buenas de que termines dándote cuenta de que solo quieres que ese cierre sea un método diferente que puedes utilizar en todas partes. Pero entonces será muy fácil hacerlo sin cambiar el rest del código estructuralmente. ¿Derecha?

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

No puedo evitar preguntarme ( advertencia: un poco de ironía / sarcasmo por delante) por qué en la tierra hacer todo este esfuerzo para básicamente reemplazar lo siguiente:

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

… con alguna variación loca del siguiente olor a código, me refiero al ejemplo, solo para fingir que estás guardando unas pocas teclas.

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

Porque ciertamente no es automáticamente más legible.

De acuerdo, dejé las tres instancias idénticas de /* write to a log, whatever... */ return; del primer ejemplo.

Pero ese es mi punto. Todos ustedes han oído hablar de funciones / métodos, ¿verdad? Seriamente. Escriba una función común de ErrorHandler y, como, ErrorHandler desde cada bloque catch.

Si me preguntas, el segundo ejemplo (con las palabras clave if y is ) es significativamente menos legible y, al mismo tiempo, significativamente más propenso a errores durante la fase de mantenimiento de tu proyecto.

La fase de mantenimiento, para cualquier persona que sea relativamente nueva en progtwigción, abarcará el 98.7% o más de la duración total de su proyecto, y el pobre idiota que hace el mantenimiento seguramente será otra persona que usted. Y hay una gran posibilidad de que gasten el 50% de su tiempo en el trabajo maldiciendo su nombre.

Y, por supuesto, FxCop te ladra y tienes que agregar un atributo a tu código que tiene que ver exactamente con el progtwig en ejecución, y solo está ahí para decirle a FxCop que ignore un problema que en el 99.9% de los casos es totalmente corregir en marcar. Y, lo siento, podría estar equivocado, pero ¿ese atributo “ignorar” no se comstack en realidad en tu aplicación?

¿Sería más legible poner toda la prueba if en una línea? No lo creo. Quiero decir, tuve otro progtwigdor que argumentó vehementemente hace mucho tiempo que poner más código en una línea lo haría “correr más rápido”. Pero, por supuesto, estaba completamente loco. Tratando de explicarle (con cara seria, lo cual era un reto) cómo el intérprete o comstackdor dividiría esa larga línea en declaraciones discretas de una instrucción por línea, esencialmente idéntica al resultado si hubiera seguido adelante y Acabo de hacer que el código sea legible en lugar de tratar de superar al comstackdor, no tuvo ningún efecto sobre él en absoluto. Pero yo divago.

¿Cuánto menos legible se obtiene cuando agrega tres tipos más de excepciones, un mes o dos a partir de ahora? (Respuesta: se vuelve mucho menos legible).

Uno de los puntos principales, en realidad, es que la mayor parte del objective de formatear el código fuente textual que estamos viendo todos los días es hacer que sea realmente obvio para otros seres humanos lo que realmente está sucediendo cuando se ejecuta el código. Porque el comstackdor convierte el código fuente en algo totalmente diferente y no le importa el estilo de formato de su código. Así que todo en una línea es una mierda, también.

Solo digo…

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

Como han señalado otros, puede tener una instrucción if dentro de su bloque catch para determinar qué está pasando. C # 6 admite filtros de excepción, por lo que lo siguiente funcionará:

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

El método MyFilter podría verse más o menos así:

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

Alternativamente, esto se puede hacer todo en línea (el lado derecho de la sentencia when solo tiene que ser una expresión booleana).

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

Esto es diferente de usar una instrucción if desde dentro del bloque catch , usar filtros de excepción no desenrollará la stack.

Puede descargar Visual Studio 2015 para verificarlo.

Si desea continuar usando Visual Studio 2013, puede instalar el siguiente paquete nuget:

Install-Package Microsoft.Net.Compilers

Al momento de escribir esto, esto incluirá soporte para C # 6.

Hacer referencia a este paquete hará que el proyecto se construya utilizando la versión específica de los comstackdores C # y Visual Basic que se incluyen en el paquete, a diferencia de cualquier versión instalada del sistema.

Desafortunadamente, no en C #, ya que necesitaría un filtro de excepción para hacerlo y C # no expone esa característica de MSIL. VB.NET tiene esta capacidad, por ejemplo, por ejemplo

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

Lo que podría hacer es usar una función anónima para encapsular su código on-error, y luego llamarlo en esos bloques catch específicos:

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

En aras de la integridad, desde .NET 4.0, el código puede reescribirse como sigue:

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

TryParse nunca arroja excepciones y devuelve falso si el formato es incorrecto, configurando WebId en Guid.Empty .


Desde C # 7 puedes evitar introducir una variable en una línea separada:

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

También puede crear métodos para analizar tuplas devueltas, que aún no están disponibles en .NET Framework a partir de la versión 4.6:

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

Y úsalos así:

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

La siguiente actualización inútil a esta respuesta inútil se produce cuando la deconstrucción de los parámetros de salida se implementa en C # 12. 🙂

Si puede actualizar su aplicación a C # 6, tiene suerte. La nueva versión de C # ha implementado filtros de excepción. Entonces puedes escribir esto:

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

Algunas personas piensan que este código es el mismo que

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

Pero no lo es. En realidad, esta es la única característica nueva en C # 6 que no es posible emular en versiones anteriores. Primero, un nuevo lanzamiento significa más sobrecarga que omitir la captura. En segundo lugar, no es semánticamente equivalente. La nueva característica conserva intacta la stack cuando depura tu código. Sin esta característica, el volcado de emergencia es menos útil o incluso inútil.

Vea una discusión sobre esto en CodePlex . Y un ejemplo que muestra la diferencia .

Si no desea usar una instrucción if dentro de los ámbitos de catch , en C# 6.0 puede usar la syntax de Exception Filters que ya era compatible con CLR en las versiones previas, pero que solo existía en VB.NET / MSIL :

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

Este código capturará la Exception solo cuando sea una InvalidDataException o ArgumentNullException .

En realidad, puedes poner básicamente cualquier condición dentro de esa cláusula when :

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

Tenga en cuenta que, a diferencia de una instrucción if dentro del catch la catch , los Exception Filters no pueden arrojar Exceptions , y cuando lo hacen, o cuando la condición no es true , la siguiente condición de catch se evaluará en su lugar:

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

Salida: captura general.

Cuando hay más de un Exception Filter true , el primero será aceptado:

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

Salida: captura.

Y como puede ver en el MSIL el código no se traduce en declaraciones if , sino en Filters , y no se pueden lanzar Exceptions dentro de las áreas marcadas con Filter 1 y Filter 2 pero el filtro que lanza la Exception fallará, también el último el valor de comparación empujado a la stack antes de que el comando del endfilter final determine el éxito / falla del filtro ( Catch 1 XOR Catch 2 se ejecutará en consecuencia):

Los filtros de excepción MSIL

Además, específicamente Guid tiene el método Guid.TryParse .

La respuesta aceptada parece aceptable, excepto que CodeAnalysis / FxCop se quejará del hecho de que está detectando un tipo de excepción general.

Además, parece que el operador “es” podría degradar ligeramente el rendimiento.

CA1800: No lanzar innecesariamente dice “considere probar el resultado del operador ‘como’ en su lugar”, pero si lo hace, escribirá más código que si captura cada excepción por separado.

De todos modos, esto es lo que haría:

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

Esta es una variante de la respuesta de Matt (creo que esto es un poco más limpio) … use un método:

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

Se lanzarán otras excepciones y el código WebId = Guid.Empty; no será golpeado Si no desea que otras excepciones bloqueen su progtwig, simplemente agregue esto DESPUÉS de las otras dos capturas:

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

@Micheal

Versión ligeramente revisada de su código:

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

Las comparaciones de cadenas son feas y lentas.

en C # 6, el enfoque recomendado es usar filtros de excepción, aquí hay un ejemplo:

  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 respuesta de Joseph Daigle es una buena solución, pero encontré que la siguiente estructura es un poco más ordenada y menos propensa a errores.

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

Hay algunas ventajas de invertir la expresión:

  • Una statement de devolución no es necesaria
  • El código no está nested
  • No hay riesgo de olvidar las afirmaciones de “arrojar” o “devolver” que en la solución de José están separadas de la expresión.

Incluso se puede compactar a una sola línea (aunque no es muy bonita)

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

Editar: El filtrado de excepciones en C # 6.0 hará que la syntax sea un poco más limpia y viene con una serie de otros beneficios sobre cualquier solución actual. (más notablemente, dejar la stack ileso)

Así es como se vería el mismo problema usando la syntax de C # 6.0:

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

Qué tal si

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

Advirtió y advirtió: Sin embargo, otro estilo amable y funcional.

Lo que está en el enlace no responde su pregunta directamente, pero es trivial ampliarlo para que se vea así:

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

(Básicamente proporciona otra sobrecarga de Catch vacía que se devuelve)

La gran pregunta es por qué . No creo que el costo supere la ganancia aquí 🙂

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

Actualización 2015-12-15: Consulte https://stackoverflow.com/a/22864936/1718702 para C # 6. Es un limpiador y ahora es estándar en el lenguaje.

Dirigido a personas que desean una solución más elegante para atrapar una vez y filtrar excepciones, utilizo un método de extensión como se demuestra a continuación.

Ya tenía esta extensión en mi biblioteca, originalmente escrita para otros fines, pero funcionó perfectamente para la verificación de type de excepciones. Además, imho, parece más limpio que un montón de || declaraciones. Además, a diferencia de la respuesta aceptada, prefiero el manejo de excepciones explícito, así que ex is ... tenía un comportamiento indeseable ya que las clases derivadas son asignables a los tipos padre).

Uso

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

Extensión de IsAnyOf.cs (consulte el ejemplo completo de gestión de errores para dependencias)

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

Ejemplo de manejo completo de errores (copiar y pegar a la nueva aplicación de consola)

 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> 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.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 { ///  /// 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, 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 { ///  /// 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, 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)); } ///  /// 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 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)); } } } 

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+. Tu puedes hacer

 try { WebId = new Guid(queryString["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 compile, say:

 // Won't compile... 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 compile – 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 compiler to do something like this (well it’s not entirely correct, but it’s the closest possible thing I guess):

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

The reason that this won’t compile 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 compiler 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 branches. Aquí va:

  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?

En resumen, 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(queryString["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(queryString["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 compiler 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:

Jerarquía de excepciones

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?

P.ej:

 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(queryString["web"]); } catch (FormatException) { WebId = Guid.Empty; } try { WebId = new Guid(queryString["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"); } }