Se desaconseja simplemente atrapar System.Exception
. En cambio, solo se deben detectar las excepciones "conocidas".
Ahora, esto a veces conduce a un código repetitivo innecesario, por ejemplo:
try
{
WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
WebId = Guid.Empty;
}
catch (OverflowException)
{
WebId = Guid.Empty;
}
Me pregunto: ¿hay alguna forma de detectar ambas excepciones y solo llamar a la WebId = Guid.Empty
llamada una vez?
El ejemplo dado es bastante simple, ya que es solo a GUID
. Pero imagine el código en el que modifica un objeto varias veces, y si una de las manipulaciones falla de la manera esperada, desea "restablecer" el object
. Sin embargo, si hay una excepción inesperada, todavía quiero lanzar eso más alto.
c#
.net
exception
exception-handling
Michael Stum
fuente
fuente
AggregateException
: Lanzar una AggregateException en mi propio códigoRespuestas:
Captura
System.Exception
y enciende los tiposfuente
EDITAR: 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 presentarí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 estar en desacuerdo:
ORIGINAL:
Sé que llego un poco tarde a la fiesta aquí, pero humo santo ...
Pasando 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 excepción 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 todo el lugar. Pero entonces será muy fácil hacerlo sin cambiar realmente el resto del código estructuralmente. ¿Derecha?
No puedo evitar preguntarme ( advertencia: un poco de ironía / sarcasmo por delante) por qué hacer todo este esfuerzo para reemplazar básicamente lo siguiente:
... con alguna variación loca de este próximo código de olor, quiero decir ejemplo, solo para fingir que estás guardando algunas teclas.
Porque ciertamente no es automáticamente más legible.
Por supuesto, dejé las tres instancias idénticas
/* write to a log, whatever... */ return;
del primer ejemplo.Pero ese es mi punto. Ustedes han oído hablar de funciones / métodos, ¿verdad? Seriamente. Escriba una
ErrorHandler
función común y, como, llámela desde cada bloque catch.Si me pregunta, el segundo ejemplo (con las palabras clave
if
yis
) 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 general de su proyecto, y el pobre imbécil que hace el mantenimiento seguramente será alguien más que usted. Y es muy probable que pasen el 50% de su tiempo en el trabajo maldiciendo su nombre.
Y, por supuesto, FxCop te ladra, por lo que también debes agregar un atributo a tu código que tenga que ver exactamente 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 marcar. Y, lo siento, podría estar equivocado, pero ¿ese atributo de "ignorar" no termina realmente compilado en su aplicación?
¿Poner la
if
prueba completa en una línea la haría más legible? No lo creo. Quiero decir, hace mucho tiempo que otro programador argumentó con vehemencia 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 una cara seria, lo cual fue un desafío) cómo el intérprete o el 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 solo hizo que el código fuera legible en lugar de tratar de superar al compilador, no tuvo ningún efecto en él. Pero yo divago.¿Cuánto menos legible es esto cuando agrega tres tipos de excepción más, dentro de un mes o dos? (Respuesta: se vuelve mucho menos legible).
Uno de los puntos principales, realmente, es que la mayoría del punto de formatear el código fuente textual que todos estamos viendo todos los días es hacer que sea realmente obvio para otros seres humanos lo que realmente sucede cuando se ejecuta el código. Debido a que el compilador convierte el código fuente en algo totalmente diferente y no podría importarle menos su estilo de formato de código. Así que todo en una línea también apesta totalmente.
Solo digo...
fuente
Exception
sy verificar el tipo. Pensé que limpiaba el código, pero algo me mantuvo volviendo a la pregunta y realmente leí las otras respuestas a la pregunta. Lo mordí por un tiempo, pero tengo que estar de acuerdo contigo. Es más fácil de leer y mantener usar una función para secar su código que para atrapar todo, verificar el tipo comparándolo con una lista, envolviendo código y arrojando. Gracias por llegar tarde y proporcionar una opción alternativa y sensata (IMO). +1.throw;
. Tendría que repetir esa línea de código en cada bloque catch (obviamente no es el fin del mundo, pero vale la pena mencionarlo, ya que es un código que debería repetirse).throw();
declaración adicional en cada bloque de captura es un pequeño precio a pagar, IMO, y aún lo deja en la posición de hacer una limpieza adicional específica del tipo de excepción si es necesario.Func<Exception, MyEnumType>
lugar deAction<Exception>
. Eso esFunc<T, Result>
, conResult
ser el tipo de retorno.Como otros han señalado, puede tener una
if
declaración dentro de su bloque catch para determinar qué está sucediendo. C # 6 admite filtros de excepción, por lo que funcionará lo siguiente:El
MyFilter
método podría verse así:Alternativamente, todo esto se puede hacer en línea (el lado derecho de la instrucción when solo tiene que ser una expresión booleana).
Esto es diferente de usar una
if
declaración desde dentro delcatch
bloque, usar filtros de excepción no desenrollará la pila.Puede descargar Visual Studio 2015 para ver esto.
Si desea continuar usando Visual Studio 2013, puede instalar el siguiente paquete nuget:
Al momento de escribir, esto incluirá soporte para C # 6.
fuente
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 ej.
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 específicos:
fuente
throw e;
destruye stacktrace y callstack,throw;
destruye "solo" callstack (¡inutilizando los volcados de memoria!) ¡Una muy buena razón para no usar ninguno si se puede evitar!En aras de la integridad, desde .NET 4.0 el código puede reescribirse como:
TryParse nunca arroja excepciones y devuelve falso si el formato es incorrecto, configurando WebId en
Guid.Empty
.Desde C # 7 , puede evitar introducir una variable en una línea separada:
También puede crear métodos para analizar tuplas de retorno, que aún no están disponibles en .NET Framework a partir de la versión 4.6:
Y úsalos así:
La próxima actualización inútil de esta respuesta inútil se produce cuando se implementa la deconstrucción de parámetros externos en C # 12. :)
fuente
Guid.TryParse
nunca regresaGuid.Empty
. Si la cadena tiene un formato incorrecto, establece elresult
parámetro de salida enGuid.Empty
, pero regresafalse
. Lo menciono porque he visto código que hace cosas al estilo deGuid.TryParse(s, out guid); if (guid == Guid.Empty) { /* handle invalid s */ }
, lo que generalmente es incorrecto sis
pudiera ser la representación de cadenaGuid.Empty
.if( Guid.TryParse(s, out guid){ /* success! */ } else { /* handle invalid s */ }
, lo que no deja ambigüedad como el ejemplo roto donde el valor de entrada podría ser la representación de cadena de un Guid.Los filtros de excepción ahora están disponibles en c # 6+. Tu puedes hacer
En C # 7.0+, también puede combinar esto con la coincidencia de patrones
fuente
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:
Algunas personas piensan que este código es el mismo que
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 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 por caída es menos útil o incluso inútil.
Vea una discusión sobre esto en CodePlex . Y un ejemplo que muestra la diferencia .
fuente
Si no desea utilizar una
if
declaración dentro de loscatch
alcances, en laC# 6.0
que puede utilizarException Filters
la sintaxis que ya fue apoyada por el CLR en versiones vistas previas, pero sólo existía enVB.NET
/MSIL
:Este código capturará el
Exception
único cuando es unInvalidDataException
oArgumentNullException
.En realidad, puedes poner básicamente cualquier condición dentro de esa
when
cláusula:Tenga en cuenta que, a diferencia de una
if
declaración dentro delcatch
alcance del 's,Exception Filters
no se puede lanzarExceptions
, y cuando lo hacen, o cuando la condición no estrue
, la siguientecatch
condición se evaluará en su lugar:Cuando haya más de
true
Exception Filter
uno, se aceptará el primero:Y como puede ver en el
MSIL
código, el código no se traduce aif
declaraciones, sino aFilters
, yExceptions
no se puede lanzar desde las áreas marcadas conFilter 1
y,Filter 2
pero el filtro que arrojaException
fallará, también el último valor de comparación empujado a la pila antes delendfilter
comando determinará el éxito / falla del filtro (Catch 1
XORCatch 2
se ejecutará en consecuencia):Además, específicamente
Guid
tiene elGuid.TryParse
método.fuente
Con C # 7, la respuesta de Michael Stum se puede mejorar manteniendo la legibilidad de una declaración de cambio:
Y con C # 8 como expresión de cambio:
fuente
when
es mucho más elegante / apropiada que un interruptor.catch
bloque diferente , o de todos modos necesitaría lanzarlo. En mi experiencia, unthrow;
en sucatch
bloque es probablemente un olor a código.La respuesta aceptada parece aceptable, excepto que CodeAnalysis / FxCop se quejará del hecho de que está detectando un tipo de excepción general.
Además, parece que el operador "es" podría degradar ligeramente el rendimiento.
CA1800: No emitir innecesariamente dice "considere probar el resultado del operador 'como' en su lugar", pero si lo hace, escribirá más código que si detecta cada excepción por separado.
De todos modos, esto es lo que haría:
fuente
is
operador puede tener un ligero impacto negativo en el rendimiento, también es cierto que un controlador de excepciones no es el lugar para preocuparse demasiado por optimizar el rendimiento. Si su aplicación está pasando tanto tiempo en controladores de excepciones que la optimización del rendimiento allí marcaría una diferencia real en el rendimiento de la aplicación, entonces hay otros problemas de código a tener en cuenta. Dicho esto, todavía no me gusta esta solución porque pierdes el seguimiento de la pila y porque la limpieza se elimina contextualmente de la declaración catch.is
operador degrada el rendimiento es si luego realiza unaas
operación (por lo tanto, califican la regla innecesariamente ). Si todo lo que está haciendo es probar el yeso sin necesidad de realizarlo, entonces elis
operador es exactamente lo que desea usar.en C # 6, el enfoque recomendado es usar filtros de excepción, aquí hay un ejemplo:
fuente
Esta es una variante de la respuesta de Matt (creo que esto es un poco más limpio) ... use un método:
Se lanzarán otras excepciones y no se aplicará el código
WebId = Guid.Empty;
. Si no desea que otras excepciones bloqueen su programa, simplemente agregue esto DESPUÉS de las otras dos capturas:fuente
WebId = Guid.Emtpy
en el caso de que no se haya producido ninguna excepción.return
a mi respuesta. Gracias por el aporte.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.
Hay algunas ventajas de invertir la expresión:
Incluso se puede compactar a una sola línea (aunque no muy bonita)
Editar: el filtro de excepción en C # 6.0 hará que la sintaxis sea un poco más limpia y viene con una serie de 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:
fuente
return
, aunque invertir la condición también es un poco mejor.@Micheal
Versión ligeramente revisada de su código:
Las comparaciones de cadenas son feas y lentas.
fuente
throw new FormatException();
conthrow new NewlyDerivedFromFormatException();
y sin romper el código usando la biblioteca, y será válido para todo el manejo de casos, excepto cuando alguien utiliza una excepción==
en lugar deis
(o, simplemente,catch (FormatException)
).Qué tal si
fuente
fuente
Precaución y advertencia: otro estilo amable y funcional.
Lo que está en el enlace no responde su pregunta directamente, pero es trivial extenderlo para que se vea así:
(Básicamente, proporcione otra
Catch
sobrecarga vacía que se devuelva)La pregunta más grande a esto es por qué . No creo que el costo supere la ganancia aquí :)
fuente
Actualización 2015-12-15: Consulte https://stackoverflow.com/a/22864936/1718702 para C # 6. Es un limpiador y ahora estándar en el lenguaje.
Dirigido a personas que desean una solución más elegante. para atrapar una vez y filtrar excepciones, utilizo un método de extensión como se muestra a continuación.
Ya tenía esta extensión en mi biblioteca, originalmente escrita para otros fines, pero funcionó perfectamente para
type
verificar las excepciones. Además, en mi 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 queex is ...
tuve un comportamiento indeseable ya que las clases derivadas se pueden asignar a los tipos principales).Uso
Extensión IsAnyOf.cs (Ver ejemplo de manejo de errores completo para dependencias)
Ejemplo completo de manejo de errores (copiar y pegar en la nueva aplicación de consola)
Dos muestras de pruebas de unidad NUnit
El comportamiento de coincidencia para los
Exception
tipos es exacto (es decir, un hijo NO ES una coincidencia para ninguno de sus tipos principales).fuente
when
, como lo haría con cualquier versión decatch (Exception ex) {if (...) {/*handle*/} throw;}
. El valor real dewhen
es que el filtro se ejecuta antes de que se detecte la excepción , evitando así la corrupción de gastos / apilamiento de un nuevo lanzamiento. Aprovecha una función CLR que solo era accesible anteriormente para VB y MSIL.IsAnyOf
método se puede reescribir simplementep_comparisons.Contains(p_parameter)
Como sentí que estas respuestas solo tocaban la superficie, intenté profundizar un poco más.
Entonces, lo que realmente queremos hacer es algo que no se compila, digamos:
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 del depurador, fealdad)
Entonces, ¿por qué este código no se compila, y cómo podemos hackearlo de tal manera que lo haga?
Si miramos el código, lo que realmente nos gustaría hacer es reenviar la llamada. Sin embargo, de acuerdo con la Partición II de MS, los bloques del controlador de excepciones IL no funcionarán así, lo que en este caso tiene sentido porque eso implicaría que el objeto '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 parecido posible):
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', use eso para que una variable contenga ambas excepciones, y luego maneje solo las dos excepciones que están atrapadas. 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 'Excepción' correcto. Para controlar el flujo del código, podemos agregar algunas ramas. Aquí va:
Las desventajas obvias para esto son que no podemos volver a tirar adecuadamente y, bueno, seamos honestos, 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:
Eso deja solo el 're-tiro'. Para que esto funcione, debemos ser capaces de realizar el manejo dentro del bloque 'catch', y la única forma de hacer que esto funcione es mediante la captura de un objeto 'Exception'.
En este punto, podemos agregar una función separada que maneja los diferentes tipos de Excepciones usando la resolución de sobrecarga, o para manejar la Excepción. Ambos tienen desventajas. Para comenzar, esta es la forma de hacerlo con una función auxiliar:
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:
Entonces para concluir:
fuente
Este es un problema clásico que todo desarrollador de C # enfrenta eventualmente.
Déjame dividir tu pregunta en 2 preguntas. El primero,
¿Puedo atrapar varias excepciones a la vez?
En resumen, no.
Lo que lleva a la siguiente pregunta,
¿Cómo evito escribir código duplicado dado que no puedo atrapar varios tipos de excepción en el mismo bloque catch ()?
Dada su muestra específica, donde el valor de reserva es barato de construir, me gusta seguir estos pasos:
Entonces el código se ve así:
Si se produce alguna excepción, WebId nunca se establece en el valor medio construido y permanece Guid.Empty.
Si construir el valor de reserva es costoso y restablecer un valor es mucho más barato, entonces movería el código de reinicio a su propia función:
fuente
Entonces, ¿estás 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:
Me pregunto por qué nadie notó esa duplicación de código.
Desde C # 6, además, tiene los filtros de excepción como ya han mencionado otros. Para que pueda modificar el código anterior a esto:
fuente
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 catch, 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 otras declaraciones catch y obviamente obtendrá errores del compilador; sin embargo, si invierte el orden, puede encadenar sus declaraciones catch (un poco de antipatrón, creo ) puede colocar el tipo de excepción general en la parte inferior y esto capturará las excepciones que no se adaptaron más arriba en su bloque try..catch:
Recomiendo encarecidamente que la gente revise este documento de MSDN:
Jerarquía de excepciones
fuente
¿Quizás 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:
Cómo lo haría, tratar de encontrar el patrón simple es hermoso
fuente
Tenga en cuenta que encontré una forma de hacerlo, pero esto se parece más al material para The Daily WTF :
fuente
Vale la pena mencionar 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 emitir 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ó.
fuente
Quiero sugerir la respuesta más corta (un estilo funcional más ):
Para esto, debe crear varias sobrecargas del método "Catch", similares a System.Action:
y así tantos como desees. Pero debe hacerlo una vez y puede usarlo en todos sus proyectos (o, si creó un paquete nuget, podríamos usarlo también).
Y la implementación de CatchMany:
pd No he realizado 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.
fuente
Simplemente llame al intento y atrape dos veces.
¡¡Es así de sencillo!!
fuente
En c # 6.0, los filtros de excepción son mejoras para el manejo de excepciones
fuente
catch (HttpException e) when e.GetHttpCode() == 400 { WriteLine("Bad Request"; }