¿Detectar varias excepciones a la vez?

Se desaconseja simplemente atrapar System.Exception. En su lugar, solo deben detectarse las excepciones "conocidas".

Ahora bien, 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 manera de detectar ambas excepciones y solo llamar al WebId = Guid.Empty llamar una vez?

El ejemplo dado es bastante simple, ya que es solo un GUID. Pero imagina un código en el que modificas un objeto varias veces, y si una de las manipulaciones falla de forma esperada, quieres "restablecer" el object. Sin embargo, si hay una excepción inesperada, todavía quiero arrojar eso más alto.

preguntado el 25 de septiembre de 08 a las 18:09

Si está utilizando .net 4 y superior, prefiero usar aggregateexception msdn.microsoft.com/en-us/library/system.aggregateexception.aspx -

Bepenfriends- Desde Sistema.Guid no tira Excepción agregada, sería genial si usted (o alguien) pudiera publicar una respuesta que muestre cómo lo envolvería en una AggregateException, etc.

"Se desaconseja simplemente detectar System.Exception". -y si el método puede lanzar 32 tipos de excepciones, ¿qué se hace? escribir capturas para cada uno de ellos por separado? -

Si un método arroja 32 tipos diferentes de excepciones, está mal escrito. O no detecta las excepciones que hacen sus propias llamadas, está haciendo MUCHO demasiado en un método, o la mayoría / todos esos 32 deberían ser una única excepción con un código de razón. -

28 Respuestas

lucha System.Exception y enciende los tipos

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

    throw;
}

respondido 26 nov., 16:01

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

Excepto que todavía odio el diseño de una línea larga y personalmente distribuiría el código de la siguiente manera. Creo que esto es tan funcional como estético, ya que creo que mejora la comprensión. Algunos pueden no estar de acuerdo:

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

ORIGINAL:

Sé que llego un poco tarde a la fiesta aquí, pero humo santo ...

Yendo directo al grano, este tipo de duplicación de una respuesta anterior, pero si realmente desea realizar una acción común para varios tipos de excepciones y mantener todo ordenado y ordenado dentro del alcance de un método, ¿por qué no usar una lambda? / cierre / función en línea para hacer algo como lo siguiente? Quiero decir, es muy probable que termines dándote cuenta de que solo quieres que ese cierre sea un método separado que puedas utilizar en todas partes. Pero entonces será muy fácil hacerlo sin cambiar el resto del código estructuralmente. ¿Derecha?

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 preguntarmeadvertencia: un poco de ironía / sarcasmo por delante) por qué diablos 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 de este siguiente olor a código, me refiero a un ejemplo, solo para fingir que está guardando algunas pulsaciones de 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.

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

Pero ese es mi punto. Todos han oído hablar de funciones / métodos, ¿verdad? En serio. Escribe un común ErrorHandler función y, como, llamarlo desde cada bloque de captura.

Si me preguntas, el segundo ejemplo (con el if y is palabras clave) 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 cualquiera que sea relativamente nuevo en la programación, comprenderá el 98.7% o más de la vida útil total de su proyecto, y el pobre idiota que hace el mantenimiento seguramente será otra persona que no sea usted. Y es muy probable que pasen el 50% de su tiempo en el trabajo maldiciendo tu nombre.

Y, por supuesto, FxCop te ladra y tienes que hacerlo Además, agregue un atributo a su código que tenga exactamente 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 al marcar. Y, lo siento, podría estar equivocado, pero ¿ese atributo "ignorar" no termina realmente compilado en su aplicación?

Pondría todo if prueba en una línea para hacerlo más legible? No lo creo. Quiero decir, tuve otro programador que argumentó con vehemencia una vez 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. Tratar de explicarle (con seriedad, lo cual fue un desafío) cómo el intérprete o compilador dividiría esa larga línea en declaraciones discretas de una instrucción por línea, esencialmente idénticas al resultado si hubiera seguido adelante y simplemente hizo que el código fuera legible en lugar de intentar superar en inteligencia al compilador, no tuvo ningún efecto en él. Pero yo divago.

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

Uno de los puntos principales, en realidad, es que la mayor parte del objetivo de formatear el código fuente textual que todos miramos 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 compilador convierte el código fuente en algo totalmente diferente y no podría importarle menos el estilo de formato de su código. Así que todo en una sola línea también es una mierda.

Solo digo...

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

Respondido 14 Feb 17, 20:02

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

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

La MyFilter El método podría verse así:

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

Alternativamente, todo esto se puede hacer en línea (el lado derecho de la declaració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 un if declaración desde dentro de la catch bloquear, usando filtros de excepción no desenrollar la pila.

Puede descargar Visual Studio 2015 para comprobar esto.

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

Paquete de instalación Microsoft.Net.Compilers

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

Hacer referencia a este paquete hará que el proyecto se compile utilizando la 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.

Respondido 06 Oct 15, 19:10

Desafortunadamente, no en C #, ya que necesitaría un filtro de excepción para hacerlo y C # no expone esa característica de MSIL. Sin embargo, VB.NET 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();
}

Respondido el 25 de Septiembre de 08 a las 22:09

En aras de la integridad, ya que .NET 4.0 el código se puede reescribir como:

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

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


Como 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 que regresan, 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 utilícelos así:

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

La próxima actualización inútil de esta respuesta inútil se produce cuando se implementa la deconstrucción de los parámetros de salida en C # 12. :)

respondido 18 mar '17, 12:03

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

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

En C # 7.0+, también puede combinar esto con la coincidencia de patrones

try
{
   await Task.WaitAll(tasks);
}
catch (Exception ex) when( ex is AggregateException ae &&
                           ae.InnerExceptions.Count > tasks.Count/2)
{
   //More than half of the tasks failed maybe..? 
}

Respondido 11 Jul 19, 15:07

Se prefiere este método no solo porque es simple y claro, sino que tampoco tiene que desenrollar la pila si no se cumplen las condiciones, lo que proporciona un mejor rendimiento e información de diagnóstico en comparación con el relanzamiento. - joe

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 relanzamiento significa más sobre la cabeza que saltarse la captura. En segundo lugar, no es semánticamente equivalente. La nueva característica conserva la pila intacta cuando está depurando su código. Sin esta característica, el volcado de memoria es menos útil o incluso inútil.

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

Respondido 01 Abr '15, 17:04

Throw sin excepción conserva la pila, pero "throw ex" la sobrescribirá. - Ivan

Si no desea utilizar un if declaración dentro de la catch alcances, in C# 6.0 puedes usar Exception Filters sintaxis que ya era compatible con CLR en versiones de vista previa, 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á el Exception solo cuando es un InvalidDataException or ArgumentNullException.

De hecho, puedes poner básicamente cualquier condición dentro de ese 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 un if declaración dentro del catchalcance, Exception Filters no puedo tirar Exceptions, y cuando lo hacen, o cuando la condición no es true, La siguiente catch la condición 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");
}

Resultado: Captura general.

Cuando hay más de uno true Exception Filter - se aceptará el primero:

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: Atrapar.

Y como puedes ver en el MSIL el código no está traducido a if declaraciones, pero para Filtersy Exceptions no se puede lanzar desde dentro de las áreas marcadas con Filter 1 y Filter 2 pero el filtro arroja el Exception fallará en su lugar, también el último valor de comparación empujado a la pila antes de la endfilter El comando determinará el éxito / fracaso del filtro (Catch 1 GRATIS Catch 2 se ejecutará en consecuencia):

Filtros de excepción MSIL

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

Respondido 07 Oct 15, 19:10

Con C # 7 la respuesta de Michael Stum se puede mejorar manteniendo la legibilidad de una declaración de cambio:

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

Y con C # 8 como expresión de cambio:

catch (Exception ex)
{
    WebId = ex switch
    {
        _ when ex is FormatException || ex is OverflowException => Guid.Empty,
        _ => throw ex
    };
}

contestado el 09 de mayo de 20 a las 12:05

¿No perderás el stacktrace si throw ex? - Nechemia Hoffmann

Sí, en el ejemplo de expresión de cambio (segundo ejemplo), pierde el stacktrace. Gracias por señalar eso. (Para que quede claro: no lo pierde en el primer ejemplo) - Fabian

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

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

CA1800: No lo lances innecesariamente dice que "considere probar el resultado del operador 'como' en su lugar", pero si lo hace, estará escribiendo más código que si detecta 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
}

Respondido 09 Abr '15, 15:04

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

Respondido 07 Oct 15, 08:10

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

Respondido 11 Oct 13, 21:10

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:

  • No es necesaria una declaración de devolución
  • El código no está anidado
  • No hay riesgo de olvidar las declaraciones 'lanzar' o 'devolver' que en la solución de Joseph están separadas de la expresión.

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

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

    // Handle exception
}

Edit: La filtrado de excepciones en C # 6.0 hará que la sintaxis sea un poco más limpia y viene con un varios otros beneficios sobre cualquier solución actual. (más notablemente dejando 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
}

contestado el 23 de mayo de 17 a las 11:05

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

Respondido el 25 de Septiembre de 08 a las 22:09

¿Qué tal

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

respondido 22 mar '13, 15:03

Eso funciona solo si el Catch-Code se puede mover completamente al Try-Block. Pero el código de imagen en el que realiza múltiples manipulaciones en un objeto, y uno en el medio falla, y desea "restablecer" el objeto. - Michael Stum ♦

En ese caso, agregaría una función de reinicio y la llamaría desde múltiples bloques de captura. - Mauricio

OP ha solicitado la captura de múltiples excepciones a la vez. Los atrapas en diferentes bloques - avivgood2

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

Respondido 30 Oct 14, 11:10

Actualización para C # 9

Usando el patrón de velas del nuevas mejoras de coincidencia de patrones hecho en C # 9, puede acortar la expresión en el filtro de excepción. Ahora, la captura de múltiples excepciones es simple:

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

Respondido el 06 de enero de 21 a las 20:01

Advertido y advertido: Otro estilo amable y funcional.

Lo que hay en el enlace no responde a su pregunta directamente, pero es trivial extenderlo para que se vea así:

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

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

(Básicamente proporcione otro vacío Catch sobrecarga que se devuelve sola)

La pregunta más importante de esto es el porqué. No creo que el costo supere la ganancia aquí :)

Respondido el 20 de junio de 20 a las 10:06

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

Dirigido a personas que quieren un solución más elegante para detectar una vez y filtrar las 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 comprobar las excepciones. Además, en mi humilde opinión, parece 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 ... tuvo un comportamiento no deseado ya que las clases derivadas se pueden asignar a los tipos principales).

Uso

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

Extensión IsAnyOf.cs (consulte el ejemplo completo de manejo de errores para las 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 una 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));
        }
    }
}

Dos pruebas unitarias de NUnit de muestra

Comportamiento coincidente para Exception tipos es exacta (es decir, un hijo NO ES una coincidencia para ninguno de sus tipos principales).

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

contestado el 23 de mayo de 17 a las 11:05

Mejorar el idioma es no "más elegante". En muchos lugares, esto creó un infierno de mantenimiento. Años más tarde, muchos programadores no están orgullosos del monstruo que crearon. No es lo que estás acostumbrado a leer. Puede causar un "¿eh?" efecto, o incluso graves "WTF". A veces es confuso. Lo único que hace es hacer que el código sea mucho más difícil de comprender para aquellos que necesitan lidiar con él más adelante en el mantenimiento, solo porque un solo programador intentó ser "inteligente". A lo largo de los años, aprendí que esas soluciones "inteligentes" rara vez son también las buenas. - kaii

Como sentí que estas respuestas simplemente tocaban la superficie, intenté profundizar un poco más.

Entonces, lo que realmente querríamos hacer es algo que no se 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 detectar una excepción y verificar con un 'si' qué hacer, pero seamos honestos, realmente no queremos eso. (FxCop, problemas con el depurador, fealdad)

Entonces, ¿por qué no se compila este código y cómo podemos piratearlo de tal manera que lo haga?

Si miramos el código, lo que realmente nos gustaría hacer es desviar la llamada. Sin embargo, de acuerdo con MS Partition II, los bloques de manejadores de excepciones de IL no funcionarán así, lo que en este caso tiene sentido porque eso implicaría que el objeto de 'excepción' 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 supongo que es lo más cercano posible):

// 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 compilará es bastante obvia: ¿qué tipo y valor tendría el objeto '$ excepción' (que se almacenan aquí en las variables 'e')? La forma en que queremos que el compilador maneje esto es notar que el tipo base común de ambas excepciones es 'Excepción', usarlo para que una variable contenga ambas excepciones y luego manejar solo las dos excepciones que se detectan. 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 correcto de 'Excepción'. Para controlar el flujo del código, podemos agregar 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 una solución bastante fea. La fealdad se puede arreglar un poco realizando la eliminación de ramas, lo que hace que la solución sea un poco 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 'relanzamiento'. Para que esto funcione, necesitamos poder realizar el manejo dentro del bloque 'catch', y la única forma de hacer que esto funcione es mediante un objeto de captura 'Exception'.

En este punto, podemos agregar una función separada que maneje los diferentes tipos de Excepciones usando la 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 auxiliar:

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 Exception y manejarlo en consecuencia. 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;
    }
}

Entonces para concluir:

  • Si no queremos volver a lanzar, podríamos considerar capturar las excepciones correctas y almacenarlas de forma temporal.
  • Si el controlador es simple y queremos reutilizar el código, probablemente la mejor solución sea introducir una función auxiliar.
  • Si queremos volver a lanzar, no tenemos más remedio que poner el código en un controlador de captura de 'Excepción', que romperá FxCop y las excepciones no detectadas de su depurador.

Respondido 21 Oct 14, 10:10

Este es un problema clásico que todos los desarrolladores de C # enfrentan eventualmente.

Permítame dividir su pregunta en 2 preguntas. El primero,

¿Puedo detectar varias excepciones a la vez?

En breve, no.

Lo que lleva a la siguiente pregunta,

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

Dada su muestra específica, donde el valor de respaldo es barato de construir, me gusta seguir estos pasos:

  1. Inicialice WebId al valor de respaldo.
  2. Construya 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 {}.

Entonces el código se ve así:

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, WebId nunca se establece en el valor a medio construir y permanece Guid.Empty.

Si construir el valor de respaldo es costoso 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);
}

respondido 15 nov., 17:20

Esta es una buena "codificación ecológica", es decir, está pensando con anticipación en su código y su huella de datos y se asegura de que no se produzcan fugas de valores medio procesados. Encantado de seguir este patrón, gracias Jeffrey! - Tahir Jalid

¿Entonces está repitiendo mucho código dentro de cada cambio de excepción? Parece que extraer un método sería una buena idea, ¿no?

Entonces su 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, además tiene el filtros de excepción como ya han mencionado otros. Entonces puede modificar el código anterior a esto:

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

contestado el 23 de mayo de 17 a las 12:05

"Me pregunto por qué nadie notó esa duplicación de código". - ¿Cómo? La todo el punto de la pregunta es eliminar la duplicación de código. - marca amery

Tenga en cuenta que encontré una forma de hacerlo, pero parece más material para El diario WTF:

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

Respondido 09 Abr '15, 15:04

-1 voto, +5 WTF :-) Esto no debería haber sido marcado como una respuesta, pero es he-larious. - Aaron

No importa lo simple que podamos hacerlo. Pero no se quedó de brazos cruzados y se le ocurrió su punto de vista para resolverlo. En verdad lo aprecio. - Maxymus

Sin embargo, no haga esto en realidad, use filtros de excepción en C # 6 o cualquiera de las otras respuestas; lo puse aquí específicamente como "Esta es una forma, pero es mala y quiero hacer algo mejor". - Michael Stum ♦

¿POR QUÉ es esto malo? Me sorprendió que no pudiera usar la excepción en una declaración de cambio directamente. - MKesper

@MKesper Veo algunas razones por las que es malo. Requiere escribir los nombres de clase completamente calificados como cadenas literales, lo cual es vulnerable a errores tipográficos de los que el compilador no puede salvarlo. (Esto es significativo ya que en muchas tiendas los casos de error están menos probados y, por lo tanto, es más probable que se pasen por alto los errores triviales). Subclase de uno de los casos especificados. Y, debido a que son cadenas, herramientas como "Buscar todas las referencias" de VS pasarán por alto los casos, lo que es pertinente si desea agregar un paso de limpieza en cualquier lugar donde se detecte una excepción en particular. - marca amery

Quería agregar 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 conocer el alcance de cada tipo de excepción que está tratando de detectar.

Por ejemplo, si usa una excepción "catch-all" como Excepción Precederá a todas las demás declaraciones de captura y obviamente obtendrá errores del compilador; sin embargo, si invierte el orden, puede encadenar sus declaraciones de captura (creo que es un poco antipatrón) puede poner el catch-all Excepción escriba en la parte inferior y esto capturará las excepciones que no cubrieron más arriba en su bloque try..catch:

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

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

Jerarquía de excepciones

Respondido 16 Abr '17, 16:04

Tal vez intente 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 catch.

P.ej:

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

// ...

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

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

Respondido el 23 de enero de 18 a las 14:01

Vale la pena mencionarlo aquí. Puede responder a las múltiples combinaciones (error de excepción y mensaje de excepción).

Me encontré con un escenario de caso de uso al intentar lanzar un objeto de control en una cuadrícula de datos, con contenido como TextBox, TextBlock o CheckBox. En este caso, la excepción devuelta fue la misma, pero el mensaje varió.

try
{
 //do something
}
catch (Exception ex) when (ex.Message.Equals("the_error_message1_here"))
{
//do whatever you like
} 
catch (Exception ex) when (ex.Message.Equals("the_error_message2_here"))
{
//do whatever you like
} 

Respondido el 07 de diciembre de 18 a las 15:12

Quiero sugerir la respuesta más corta (una más estilo funcional):

        Catch<FormatException, OverflowException>(() =>
            {
                WebId = new Guid(queryString["web"]);
            },
            exception =>
            {
                WebId = Guid.Empty;
            });

Para ello, debe crear varias sobrecargas del método "Catch", similares a System.Action:

    [DebuggerNonUserCode]
    public static void Catch<TException1, TException2>(Action tryBlock,
        Action<Exception> catchBlock)
    {
        CatchMany(tryBlock, catchBlock, typeof(TException1), typeof(TException2));
    }

    [DebuggerNonUserCode]
    public static void Catch<TException1, TException2, TException3>(Action tryBlock,
        Action<Exception> catchBlock)
    {
        CatchMany(tryBlock, catchBlock, typeof(TException1), typeof(TException2), typeof(TException3));
    }

y así sucesivamente tantas como desee. Pero debe hacerlo una vez y puede usarlo en todos sus proyectos (o, si creó un paquete nuget, también podríamos usarlo).

Y la implementación de CatchMany:

    [DebuggerNonUserCode]
    public static void CatchMany(Action tryBlock, Action<Exception> catchBlock,
        params Type[] exceptionTypes)
    {
        try
        {
            tryBlock();
        }
        catch (Exception exception)
        {
            if (exceptionTypes.Contains(exception.GetType())) catchBlock(exception);
            else throw;
        }
    }

ps No he puesto comprobaciones nulas para la simplicidad del código, considere agregar validaciones de parámetros.

ps2 Si desea devolver un valor de la captura, es necesario hacer los mismos métodos de captura, pero con devoluciones y Func en lugar de Acción en los parámetros.

contestado el 27 de mayo de 19 a las 15:05

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

contestado el 20 de mayo de 15 a las 08:05

Este ejemplo no muestra ningún uso de filtros de excepción. - user247702

Esta es la forma estándar de filtrar la excepción en c # 6.0 - Kashif

Eche un vistazo de nuevo a qué son exactamente los filtros de excepción. No está utilizando un filtro de excepción en su ejemplo. Hay un ejemplo apropiado en esta respuesta publicado un año antes que el tuyo. - user247702

Un ejemplo de filtrado de excepciones sería catch (HttpException e) when e.GetHttpCode() == 400 { WriteLine("Bad Request"; } - saludar

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.