Pregunta Capturas múltiples excepciones a la vez?


Se desaconseja simplemente atrapar 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 manera de atrapar ambas excepciones y solo llamar al WebId = Guid.Empty llamar una vez?

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


1709
2017-09-25 20:56


origen


Respuestas:


Captura System.Exception y enciende los tipos

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

    throw;
}

1788
2017-09-25 21:01



EDITAR: Estoy de acuerdo con otros que dicen que, a partir de C # 6.0, los filtros de excepción ahora son una manera perfecta de hacerlo: 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 alcance 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 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 preguntarme (advertencia: un poco de ironía / sarcasmo adelante) ¿por qué ir a 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. Escribe un común ErrorHandler función y, como, llamarlo desde cada bloque catch.

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 cualquier persona que sea relativamente nueva en programació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 ademásagregue un atributo a su código que tiene precisamente 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 se compila en realidad en tu aplicación?

Sería poner todo el if prueba en una línea para que sea más legible? No lo creo. Quiero decir, tuve otro programador 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 compilador 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 compilador, no tuvo ningún efecto sobre él en absoluto. Pero yo divago.

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

Uno de los puntos principales, en realidad, es que la mayor parte del objetivo 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 compilador 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;
}

370
2017-10-12 00:24



Como otros han señalado, puedes tener un if declaración dentro de tu 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))
{
    …
}

los MyFilter método 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 un if declaración desde dentro de la catch bloquear, usando filtros de excepción no lo hará desenrolla la pila.

Tú puedes descargar Visual Studio 2015 para verificar esto.

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 el   versión específica de los compiladores C # y Visual Basic contenidos en   paquete, a diferencia de cualquier versión instalada del sistema.


239
2018-04-04 13:59



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,

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

184
2017-09-25 21:03



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

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

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


Ya que 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 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. :)


122
2018-04-13 12:18



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 pila cuando depura tu código. Sin esta característica, el volcado de emergencia es menos útil o incluso inútil.

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


62
2018-04-01 12:29



Si no quieres usar un if declaración dentro del catch alcances, en C# 6.0 puedes usar Exception Filters sintaxis que ya era compatible con el CLR en las versiones previas, pero existía solo 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 atrapará al Exception solo cuando es un InvalidDataException o ArgumentNullException.

En realidad, 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 de Exception Filters no puede tirar Exceptions, y cuando lo hacen, o cuando la condición no es true, el 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");
}

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

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

Exception Filters MSIL

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


26
2017-10-07 17:31