Estoy mirando el artículo C # - Objeto de transferencia de datos en DTO serializables.
El artículo incluye esta pieza de código:
public static string SerializeDTO(DTO dto) {
try {
XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
StringWriter sWriter = new StringWriter();
xmlSer.Serialize(sWriter, dto);
return sWriter.ToString();
}
catch(Exception ex) {
throw ex;
}
}
El resto del artículo parece sensato y razonable (para un novato), pero ese intento de atrapar arroja una excepción Wtf ... ¿No es esto exactamente equivalente a no manejar excepciones en absoluto?
Es decir:
public static string SerializeDTO(DTO dto) {
XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
StringWriter sWriter = new StringWriter();
xmlSer.Serialize(sWriter, dto);
return sWriter.ToString();
}
¿O me estoy perdiendo algo fundamental sobre el manejo de errores en C #? Es más o menos lo mismo que Java (menos las excepciones marcadas), ¿no? ... Es decir, ambos refinaron C ++.
La pregunta de desbordamiento de pila ¿ La diferencia entre volver a lanzar capturas sin parámetros y no hacer nada? parece apoyar mi opinión de que try-catch-throw es un no-op.
EDITAR:
Solo para resumir para cualquiera que encuentre este hilo en el futuro ...
NO HAGA
try {
// Do stuff that might throw an exception
}
catch (Exception e) {
throw e; // This destroys the strack trace information!
}
¡La información de seguimiento de la pila puede ser crucial para identificar la causa raíz del problema!
HACER
try {
// Do stuff that might throw an exception
}
catch (SqlException e) {
// Log it
if (e.ErrorCode != NO_ROW_ERROR) { // filter out NoDataFound.
// Do special cleanup, like maybe closing the "dirty" database connection.
throw; // This preserves the stack trace
}
}
catch (IOException e) {
// Log it
throw;
}
catch (Exception e) {
// Log it
throw new DAOException("Excrement occurred", e); // wrapped & chained exceptions (just like java).
}
finally {
// Normal clean goes here (like closing open files).
}
Capture las excepciones más específicas antes que las menos específicas (como Java).
Referencias
fuente
Respuestas:
Primero; la forma en que lo hace el código en el artículo es malo.
throw ex
restablecerá la pila de llamadas en la excepción al punto donde está esta declaración de lanzamiento; perder la información sobre dónde se creó realmente la excepción.En segundo lugar, si solo atrapa y vuelve a lanzar de esa manera, no veo ningún valor agregado, el ejemplo de código anterior sería igual de bueno (o, dado el
throw ex
bit, incluso mejor) sin el try-catch.Sin embargo, hay casos en los que es posible que desee atrapar y volver a lanzar una excepción. El registro podría ser uno de ellos:
fuente
ex
objeto, entonces no hay necesidad de instanciarlo.throw ex
no reinicia el stacktrace.No hagas esto
Perderá la información de seguimiento de la pila ...
O lo haces
O
Una de las razones por las que es posible que desee volver a lanzar es si está manejando diferentes excepciones, por ejemplo
fuente
C # (antes de C # 6) no admite "excepciones filtradas" de CIL, lo que VB sí, por lo que en C # 1-5 una razón para volver a lanzar una excepción es que no tiene suficiente información en el momento de la captura () para determinar si realmente quería atrapar la excepción.
Por ejemplo, en VB puedes hacer
... que no manejaría MyExceptions con diferentes valores de ErrorCode. En C # anterior a v6, tendría que atrapar y volver a lanzar MyException si el Código de error no era 123:
Desde C # 6.0 puede filtrar al igual que con VB:
fuente
Mi razón principal para tener código como:
es para que pueda tener un punto de interrupción en la captura, que tiene un objeto de excepción instanciado. Hago esto mucho durante el desarrollo / depuración. Por supuesto, el compilador me da una advertencia sobre todas las e no utilizadas, e idealmente deberían eliminarse antes de una versión de lanzamiento.
Sin embargo, son agradables durante la depuración.
fuente
#if DEBUG
alrededor de AMBOStry {
y} catch () {...}
es un poco desordenado y, francamente, me marea ... El preprocesador, en general, no es mi amigo.Una razón válida para volver a generar excepciones puede ser que desee agregar información a la excepción, o quizás envolver la excepción original en una de sus propias creaciones:
fuente
No exactamente, no es lo mismo. Restablece el stacktrace de la excepción. Aunque estoy de acuerdo en que probablemente sea un error y, por lo tanto, un ejemplo de código incorrecto.
fuente
No quieres tirar ex, ya que esto perderá la pila de llamadas. Consulte Manejo de excepciones (MSDN).
Y sí, el intento ... catch no está haciendo nada útil (aparte de perder la pila de llamadas, por lo que en realidad es peor, a menos que por alguna razón no quiera exponer esta información).
fuente
Un punto que la gente no ha mencionado es que, si bien los lenguajes .NET realmente no hacen una distinción adecuada, la cuestión de si uno debe tomar medidas cuando se produce una excepción y si se resolverá , en realidad son preguntas distintas. Hay muchos casos en los que uno debe tomar medidas basadas en excepciones que uno no tiene la esperanza de resolver, y hay algunos casos en los que todo lo que es necesario para "resolver" una excepción es desenrollar la pila hasta cierto punto; no se requieren acciones adicionales .
Debido a la sabiduría común de que uno solo debe "atrapar" cosas que uno puede "manejar", una gran cantidad de código que debe tomar medidas cuando ocurren excepciones, no lo hace. Por ejemplo, una gran cantidad de código adquirirá un bloqueo, colocará el objeto protegido "temporalmente" en un estado que viola sus invariantes, lo colocará en un estado legítimo y luego liberará el bloqueo antes de que alguien más pueda ver el objeto. Si ocurre una excepción mientras el objeto está en un estado peligrosamente inválido, la práctica común es liberar el bloqueo con el objeto aún en ese estado. Un patrón mucho mejor sería tener una excepción que ocurra mientras el objeto está en una condición "peligrosa" que invalida expresamente el bloqueo, por lo que cualquier intento futuro de adquirirlo fallará de inmediato.
En la mayoría de los lenguajes .NET, la única forma de que el código tome medidas basadas en una excepción es
catch
hacerlo (aunque sepa que no va a resolver la excepción), realice la acción en cuestión y luego vuelva a ejecutarlathrow
. Otro enfoque posible si al código no le importa qué excepción se produce es usar unaok
bandera con untry/finally
bloque; establezca laok
banderafalse
antes del bloque, ytrue
antes de que el bloque salga, y antes de cualquierareturn
que esté dentro del bloque. Luego, dentrofinally
, suponga que siok
no está configurado, debe haber ocurrido una excepción. Tal enfoque es semánticamente mejor que acatch
/throw
, pero es feo y es menos sostenible de lo que debería ser.fuente
Esto puede ser útil cuando su programación funciona para una biblioteca o dll.
Esta estructura de repetición puede usarse para restablecer la pila de llamadas a propósito, de modo que en lugar de ver la excepción lanzada desde una función individual dentro de la función, obtenga la excepción de la función misma.
Creo que esto solo se usa para que las excepciones lanzadas sean más claras y no entren en las "raíces" de la biblioteca.
fuente
Una posible razón para atrapar es deshabilitar los filtros de excepción que se encuentran más arriba en la pila para que no se filtren ( enlace antiguo aleatorio ). Pero, por supuesto, si esa fuera la intención, habría un comentario allí que lo diga.
fuente
Depende de lo que esté haciendo en el bloque catch, y si desea pasar el error al código de llamada o no.
Puede decir
Catch io.FileNotFoundExeption ex
y luego usar una ruta de archivo alternativa o algo así, pero aún así arrojar el error.También haciendo en
Throw
lugar deThrow Ex
permite mantener el seguimiento completo de la pila. Throw ex reinicia el seguimiento de la pila desde la instrucción throw (espero que tenga sentido).fuente
Si bien muchas de las otras respuestas brindan buenos ejemplos de por qué es posible que desee capturar una repetición de una excepción, nadie parece haber mencionado un escenario 'finalmente'.
Un ejemplo de esto es cuando tiene un método en el que establece el cursor (por ejemplo, un cursor de espera), el método tiene varios puntos de salida (por ejemplo, si () regresa;) y desea asegurarse de que el cursor se restablezca en el Fin del método.
Para hacer esto, puede envolver todo el código en un intento / captura / finalmente. Finalmente, vuelva a colocar el cursor en el cursor derecho. Para que no entierres ninguna excepción válida, vuelve a lanzarla en la captura.
fuente
catch
una parte obligatoriatry...finally
históricamente o desempeña un papel funcional en este ejemplo? - Solo verifiqué dos veces, y puedo usarlotry {} finally {}
sin el bloqueo.En el ejemplo en el código que ha publicado, de hecho, no tiene sentido capturar la excepción, ya que no se hace nada en la captura, solo se vuelve a mostrar, de hecho, hace más daño que bien ya que se pierde la pila de llamadas .
Sin embargo, detectaría una excepción para hacer algo de lógica (por ejemplo, cerrar la conexión sql del bloqueo de archivos, o simplemente un poco de registro) en caso de una excepción, devolverla al código de llamada para tratar. Esto sería más común en una capa empresarial que en el código front end, ya que es posible que desee que el codificador que implementa su capa empresarial maneje la excepción.
Para volver a iterar aunque no hay ningún punto en la captura de la excepción en el ejemplo que publicó. ¡NO lo hagas así!
fuente
Lo sentimos, pero muchos ejemplos como "diseño mejorado" todavía huelen horriblemente o pueden ser extremadamente engañosos. Tener intente {} catch {log; throw} es completamente inútil. El registro de excepciones debe hacerse en un lugar central dentro de la aplicación. de todos modos, las excepciones aumentan la traza de la pila, ¿por qué no registrarlas en algún lugar cerca de los bordes del sistema?
Se debe tener precaución cuando se serializa su contexto (es decir, DTO en un ejemplo dado) solo en el mensaje de registro. Puede contener fácilmente información confidencial que quizás no desee alcanzar las manos de todas las personas que pueden acceder a los archivos de registro. Y si no agrega ninguna información nueva a la excepción, realmente no veo el punto de ajuste de excepción. El viejo Java tiene algún punto para eso, requiere que la persona que llama sepa qué tipo de excepciones se deben esperar luego de llamar al código. Como no tiene esto en .NET, el ajuste no sirve de nada en al menos el 80% de los casos que he visto.
fuente
Además de lo que han dicho los demás, vea mi respuesta a una pregunta relacionada que muestra que atrapar y volver a lanzar no es un no-op (está en VB, pero parte del código podría ser invocado en C # desde VB).
fuente
La mayoría de las respuestas hablan del escenario catch-log-rethrow.
En lugar de escribirlo en su código, considere usar AOP, en particular Postsharp.Diagnostic.Toolkit con OnExceptionOptions IncludeParameterValue e IncludeThisArgument
fuente
Volver a generar excepciones a través de
throw
es útil cuando no tiene un código particular para manejar las excepciones actuales, o en los casos en que tiene una lógica para manejar casos de error específicos pero desea omitir todos los demás.Ejemplo:
Sin embargo, también hay otra forma de hacerlo, utilizando cláusulas condicionales en bloques catch:
fuente