Capturar múltiples excepciones a la vez?


Se desaconseja simplemente atrapar System.Exception. En cambio, solo las excepciones" conocidas " deben ser capturadas.

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 una manera de captar ambas excepciones y llamar solo 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 modifica un objeto varias veces, y si una de las manipulaciones falla de la manera esperada, desea para "restablecer" el object. Sin embargo, si hay una excepción inesperada, todavía quiero lanzar más alto.

Author: Mark Amery, 2008-09-26

26 answers

Captura System.Exception y enciende los tipos

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

    throw;
}
 1825
Author: Joseph Daigle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-26 01:53:35

EDIT: Estoy de acuerdo con otros que están diciendo que, a partir de C# 6.0, los filtros de excepción son ahora una forma perfecta de hacerlo: catch (Exception ex) when (ex is ... || ex is ... )

Excepto que todavía odio el diseño de una sola línea y personalmente establecería 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 llego un poco tarde a la fiesta aquí, pero santo fumar...

Yendo directamente al grano, esto 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 alcance de un método, ¿por qué no usar una función lambda/closure/inline para hacer algo como lo siguiente? Quiero decir, las posibilidades son bastante buenas de que termines dándote cuenta de que solo quieres hacer que el cierre sea un método separado que puedas utilizar en todo el lugar. Pero entonces será súper fácil hacerlo sin cambiar el resto del código estructuralmente. ¿Verdad?

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

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

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

...con alguna variación loca de este olor de código siguiente, me refiero a ejemplo, solo para fingir que estás guardando algunas pulsaciones de teclado.

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

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

Pero ese es mi punto. Todos ustedes han oído hablar de funciones / métodos, ¿verdad? Seriamente. Escribe una función ErrorHandler común y, como, llámala 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 su proyecto.

La fase de mantenimiento, para cualquier persona que pueda ser relativamente nueva en la programación, comprenderá el 98.7% o más de la vida útil general de su proyecto, y el pobre idiota que hace el mantenimiento es casi seguro que será alguien que no sea usted. Y hay una muy buena probabilidad de que pasen el 50% de su tiempo en el trabajo maldiciendo su nombre.

Y por supuesto FxCop ladra a usted y así que usted tiene que también añadir un atributo al código eso tiene precisamente zip que ver con el programa 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 correcto en el marcado. Y, lo siento, puede que me equivoque, pero ¿no termina ese atributo "ignorar" realmente compilado en su aplicación?

¿Poner toda la prueba if en una línea la haría más legible? No lo creo. Quiero decir, tuve otro programador vehementemente argumentar una vez hace mucho tiempo que poner más código en una línea lo haría " ejecutar más rápido."Pero, por supuesto, estaba completamente loco. Tratar de explicarle (con una cara seria which lo cual era un desafío) cómo el intérprete o compilador rompería esa larga línea en discretas instrucciones de una instrucción por línea essentially esencialmente idénticas al resultado si hubiera seguido adelante y solo hubiera hecho el código legible en lugar de tratar de superar al compilador clever no tuvo ningún efecto en él en absoluto. Pero estoy divagando.

Cuánto menos legible obtiene esto cuando agrega ¿tres tipos de excepción más, dentro de un mes o dos? (Respuesta: obtiene un lote menos legible).

Uno de los puntos principales, realmente, es que la mayor parte del punto de formatear el código fuente textual que todos estamos viendo todos los días es hacer realmente, realmente obvio para otros seres humanos lo que realmente está sucediendo cuando se ejecuta el código. Porque el compilador convierte el código fuente en algo totalmente diferente y no podría importarle menos el formato de su código estilo. Así que todo-en-una-línea totalmente apesta, 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;
}
 392
Author: Craig,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-14 20:38:54

Como otros han señalado, puede tener una instrucción if dentro de su bloque catch para determinar lo que está sucediendo. 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 entonces verse algo como esto:

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 instrucción 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 dentro del bloque catch, usando los filtros de excepción no desenrollarán la pila.

Puede descargar Visual Studio 2015 para comprobar esto.

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

Install-Package Microsoft.Net.Compilers

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

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

 258
Author: Joe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-06 18:11:12

No en C# desafortunadamente, ya que necesitaría un filtro de excepción para hacerlo y C# no expone esa característica de MSIL. VB.NET sin embargo, tiene esta capacidad, 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 de error, y luego llamarlo en esos bloques de captura específicos:

Action onError = () => WebId = Guid.Empty;
try
{
    // something
}
catch (FormatException)
{
    onError();
}
catch (OverflowException)
{
    onError();
}
 183
Author: Greg Beech,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-25 21:03:57

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

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

TryParse nunca lanza excepciones y devuelve false si el formato es incorrecto, estableciendo WebID a Guid.Empty.


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

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

También puede crear métodos para analizar las 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 usarlas como esto:

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

La siguiente actualización inútil de esta respuesta inútil viene cuando se implementa la deconstrucción de los parámetros out en C# 12. :)

 124
Author: Athari,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-18 12:40:01

Si puedes actualizar tu aplicación a C# 6 tienes suerte. La nueva versión de C# ha implementado filtros de excepción. Así que 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 es así. En realidad, esta es la única característica nueva en C# 6 que no es posible emular en versiones anteriores. Primero, un re-lanzamiento significa más sobrecarga que saltarse la captura. Segundo, no es semánticamente equivalente. La nueva característica conserva la pila intacta cuando se depurando tu código. Sin esta característica, el volcado de fallos es menos útil o incluso inútil.

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

 64
Author: Maniero,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-01 16:04:37

Si no desea utilizar una instrucción if dentro de los ámbitos catch , en C# 6.0 puede usar la sintaxis Exception Filters que ya era compatible con el CLR en las versiones de vista previa, pero 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á el Exception solo cuando es un InvalidDataException o ArgumentNullException.

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

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 ámbito de catch, Exception Filters no pueden lanzar Exceptions, y cuando lo hacen, o cuando la condición no es true, la siguiente condición catch será evaluada 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 uno true Exception Filter - 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: Catch.

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

Filtros de Excepción MSIL

También, específicamente Guid tiene Guid.TryParse método.

 28
Author: Tamir Vered,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-07 18:37:42

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

Además, parece que el operador "is" podría degradar ligeramente el rendimiento.

CA1800: No lanzar innecesariamente dice que "considere probar el resultado del operador' as 'en su lugar", pero si lo hace, estará escribiendo más código que si captura cada excepción por separado.

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

bool exThrown = false;

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

if (exThrown)
{
    // Something else
}
 19
Author: Matt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-09 14:09:55

Esta es una variante de la respuesta de Matt (siento que esto es un poco más limpio)...utilice un método:

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

    WebId = Guid.Empty;
}

Cualquier otra excepción será lanzada y el código WebId = Guid.Empty; no será golpeado. Si no desea que otras excepciones bloqueen su programa, 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
}
 18
Author: bsara,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-11 20:52:50

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

 17
Author: FlySwat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-25 21:16:23

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");
 }
 17
Author: SHM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-07 07:53:58

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 declaración de retorno no es necesaria
  • El código no está anidado
  • No hay riesgo de olvidar las declaraciones 'tirar' o 'volver' que en la solución de José están separadas de la expresión.

Incluso se puede compactar en una sola línea (aunque no muy bonito)

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 sintaxis sea un poco más limpia y viene con un número de otros beneficios sobre cualquier solución actual. (lo más notable es dejar la pila ilesa)

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

catch(Exception ex) when (ex is SomeException || ex is OtherException)
{
    // Handle exception
}
 16
Author: Stefan T,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 10:31:35

¿Qué tal

try
{
    WebId = Guid.Empty;
    WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
}
catch (OverflowException)
{
}
 13
Author: Maurice,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-22 15:58:32

Con C # 7 la respuesta de Michael Stum se puede mejorar mientras se mantiene la legibilidad de una instrucción switch:

catch (Exception ex)
{
    switch (ex)
    {
        case FormatException _:
        case OverflowException _:
            WebId = Guid.Empty;
            break;
        default:
            throw;
    }
}
 13
Author: Fabian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-05 11:43:42

Los filtros de excepción ahora están disponibles en c# 6+. Usted puede hacer

try
{
       WebId = new Guid(queryString["web"]);
}
catch (Exception ex) when(ex is FormatException || ex is OverflowException)
{
     WebId = Guid.Empty;
}
 13
Author: Mat J,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-11 13:12:05

Advirtió y Advirtió: Otro estilo amable y funcional.

Lo que hay en el enlace no responde a tu pregunta directamente, pero es trivial extenderlo para que parezca:

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

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

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

La pregunta más grande a esto es por qué. No creo que el costo supere la ganancia aquí:)

 12
Author: nawfal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-18 11:28:30
catch (Exception ex)
{
    if (!(
        ex is FormatException ||
        ex is OverflowException))
    {
        throw;
    }
    Console.WriteLine("Hello");
}
 12
Author: Konstantin Spirin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-30 11:15:16

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

Orientado a las personas que quieren una solución más elegante para capturar una vez y filtrar excepciones, utilizo un método de extensión como se muestra a continuación.

Ya tenía esta extensión en mi biblioteca, originalmente escrita para otros propósitos, pero funcionó perfectamente para type verificar las excepciones. Además, en mi humilde opinión, se ve más limpio que un montón de || declaraciones. Además, a diferencia de la respuesta aceptada, prefiero el manejo explícito de excepciones, por lo que ex is ... tenía un comportamiento indesireable, ya que las clases derrivadas son asignables a los tipos padre).

Uso

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

Es cualquiera.extensión cs (Ver Ejemplo Completo de Manejo de Errores para Dependencias)

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

Ejemplo completo de Manejo de errores (Copiar y pegar en 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<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));
        }
    }
}

Unidad de NUnit de Dos Muestras Pruebas

El comportamiento de coincidencia para los tipos Exception es exacto (ie. UN hijo NO coincide con ninguno de sus tipos padre).

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
Author: HodlDwon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 10:31:35

Dado que sentí que estas respuestas solo tocaban la superficie, intenté cavar un poco más profundo.

Así que lo que realmente queremos hacer es algo que no compile, digamos:

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

La razón por la que queremos esto es porque no queremos que el controlador de excepciones capture cosas que necesitamos más adelante en el proceso. Claro, podemos atrapar una Excepción y verificar con un " si " qué hacer, pero seamos honestos, realmente no queremos eso. (FxCop, problemas del depurador, uglyness)

Entonces por qué no este código compile - y ¿cómo podemos hack de tal manera que será?

Si miramos el código, lo que realmente nos gustaría hacer es reenviar la llamada. Sin embargo, según la partición MS II, los bloques IL exception handler no funcionarán así, lo que en este caso tiene sentido porque eso implicaría que el objeto 'exception' puede tener diferentes tipos.

O para escribirlo en código, le pedimos al compilador que haga algo como esto (bueno, no es del todo correcto, pero es el lo más cercano posible, supongo):

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

La razón por la que esto no se compila es bastante obvia: ¿qué tipo y valor tendría el objeto 'exception exception' (que se almacenan aquí en las variables 'e')? La forma en que queremos que el compilador maneje esto es tener en cuenta que el tipo base común de ambas excepciones es 'Exception', usar eso para que una variable contenga ambas excepciones, y luego manejar solo las dos excepciones que son capturadas. La forma en que esto se implementa en IL es como 'filtro', que está disponible en VB.Net.

Para que funcione en C#, necesitamos una variable temporal con el tipo base 'Exception' correcto. Para controlar el flujo del código, podemos añadir algunas ramas. 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.");

Las desventajas obvias de esto son que no podemos volver a lanzar correctamente, y-bueno, seamos honestos-que es la solución bastante fea. La uglyness se puede arreglar un pedacito realizando la eliminación de la rama, que hace la solución levemente mejor:

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

Eso deja solo el 're-throw'. Para que esto funcione, necesitamos poder realizar el manejo dentro del bloque' catch '- y la única manera de hacer que esto funcione es mediante un objeto' Exception ' catching.

En este punto, podemos agregar una función separada que maneja los diferentes tipos de Excepciones usando resolución de sobrecarga, o para manejar la Excepción. Ambos tienen desventajas. Para empezar, esta es la forma de hacerlo con una función 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.");

Y la otra solución es capturar el objeto de excepción y manejarlo consecuentemente. La traducción más literal para esto, basada en el contexto anterior es esta:

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

Así que para concluir:

  • Si no queremos volver a lanzar, podríamos considerar capturar las excepciones correctas y almacenarlas en un temporal.
  • Si el controlador es simple, y queremos reutilizar el código, la mejor solución es probablemente introducir una función auxiliar.
  • Si queremos volver a lanzar, no tenemos otra opción que poner el código en un controlador de captura' Excepción', que rompa FxCop y las excepciones no capturadas de su depurador.
 7
Author: atlaste,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-21 09:44:02

¿Entonces estás repitiendo mucho código dentro de cada cambio de excepción? Suena como extraer un método sería idea de Dios, ¿no?

Así que tu código se reduce a esto:

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

Me pregunto por qué nadie notó esa duplicación de código.

Desde C#6 tienes además los filtros de excepción como ya han mencionado otros. Así que puedes modificar el código anterior a esto:

try { ... }
catch(Exception e) when(e is Exception1 || e is Exception2)
{ 
    Reset(instance); 
}
 6
Author: HimBromBeere,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:26

Este es un problema clásico que cada desarrollador de C# enfrenta eventualmente.

Permítanme dividir su pregunta en 2 preguntas. La primera,

¿Puedo detectar varias excepciones a la vez?

En resumen, no.

Que conduce a la siguiente pregunta,

¿Cómo evito escribir código duplicado dado que no puedo capturar varios tipos de excepción en el mismo bloque catch ()?

Dada su muestra específica, donde el valor de reserva es barato de construir, I sigue estos pasos:

  1. Inicialice WebID al valor de reserva.
  2. Construye un nuevo Guid en una variable temporal.
  3. Establezca WebID en la variable temporal completamente construida. Haga de esta la declaración final del bloque try {}.

Así que el código se ve como:

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 se lanza alguna excepción, entonces WebID nunca se establece en el valor semi-construido, y permanece Guid.Vaciar.

Si la construcción del valor de reserva es costosa, y restablecer un valor es mucho más barato, entonces movería el código de restablecimiento a su propia función:

try
{
    WebId = new Guid(queryString["web"]);
    // More initialization code goes here.
}
catch (FormatException) {
    Reset(WebId);
}
catch (OverflowException) {
    Reset(WebId);
}
 5
Author: Jeffrey Rennie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-15 20:27:32

Tenga en cuenta que encontré una manera de hacerlo, pero esto se parece más al material para El Diario WTF :

catch (Exception ex)
{
    switch (ex.GetType().Name)
    {
        case "System.FormatException":
        case "System.OverflowException":
            WebId = Guid.Empty;
            break;
        default:
            throw;
    }
}
 3
Author: Michael Stum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-09 14:07:34

Quería añadir mi respuesta corta a este hilo ya largo. Algo que no se ha mencionado es el orden de precedencia de las declaraciones de captura, más específicamente debe ser consciente del alcance de cada tipo de excepción que está tratando de capturar.

Por ejemplo, si usa una excepción" catch-all " como Exception precederá a todas las demás declaraciones catch y obviamente obtendrá errores del compilador, sin embargo, si invierte el orden, puede encadenar su captura declaraciones (un poco de anti-patrón creo) puede poner el catch-all Exception tipo en la parte inferior y esto será capturar cualquier excepción que no atender a más arriba en su intento..bloque de captura:

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

Recomiendo encarecidamente a la gente que revise este documento de MSDN:

Jerarquía de excepciones

 3
Author: Tahir Khalid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-16 15:51:04

Tal vez tratar de mantener su código simple, como poner el código común en un método, como lo haría en cualquier otra parte del código que no está dentro de una cláusula de captura?

Ej:

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

// ...

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

Cómo lo haría, tratando de encontrar el simple es hermoso patrón

 2
Author: Żubrówka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-23 14:02:59

Simplemente llama al try and catch dos veces.

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

Es así de Simple!!

 -13
Author: scholar guy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-29 04:42:02

En c # 6.0, los Filtros de excepción son mejoras para el manejo de excepciones

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
Author: Kashif,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-20 07:48:59