language agnostic - ¿Por qué los buenos programadores a veces tragan silenciosamente excepciones?
language-agnostic exception-handling (19)
¿Qué tal cuando se realiza una actualización de control de progreso? He actualizado las variables de estado utilizadas por la rutina de dibujo de un control, por lo que la próxima vez que se dibuje el control mostrará el estado correcto. Entonces realizo:
Try MyControl.BeginInvoke(MyControlUpdateDelegate); Catch Ex as Exception End Try
Si el control aún existe, se actualizará. Si se elimina, no me importará si se actualiza o no. Si el control no se elimina, pero no se puede actualizar por algún otro motivo, todavía no me importa si se actualiza o no, porque de todos modos no seré capaz de hacer nada sensato al respecto.
Tenga en cuenta que podría agregar "If Not MyControl.IsDisposed Then ...", pero eso solo agregaría trabajo al caso común, y no evitaría que ocurra la excepción si el control se elimina durante el BeginInvoke.
Sé que es malo, pero he visto excepciones tragadas en el código escrito por un buen programador. Entonces me pregunto si esta mala práctica podría tener al menos un punto positivo.
En otras palabras, es malo, pero ¿por qué los buenos programadores, en raras ocasiones, lo usan?
try
{
//Some code
}
catch(Exception){}
Al buscar en mi propio código, encontré un lugar en mi código de registro donde, después de no poder escribir en un archivo y no escribir en el registro de eventos, se traga el error ya que no hay lugar para informarlo. Ese es un ejemplo: no hay muchos otros.
Creo que es debido a las excepciones comprobadas de Java. Personalmente los odio por esto porque las personas tienden a pensar que necesitan código de manejo de excepciones en todas partes. IMO en el 99% del código, simplemente deberías arrojar tu excepción a la pila y no manejarla. Me encantaría que la firma predeterminada para cualquier método nuevo creado tuviera ''Excepción'' en ella para que no tenga que lidiar con excepciones a menos que lo desee.
OMI solo necesita manejo de excepciones en dos lugares: 1. Para la limpieza de recursos, como cerrar una secuencia de entrada, conexión a la base de datos o eliminar un archivo. 2. En la parte superior de la pila donde atrapas la excepción y ya sea que la registres o la muestres al usuario.
Hay lugares en los que realmente no es necesario hacer nada en la captura, como manejar la InterruptedException de Thread.sleep () y al menos debe tener un comentario para dejar en claro que realmente no quiere que pase nada. ahí.
Debido a la naturaleza de las aplicaciones que escribo, prácticamente todas las excepciones que capto deben registrarse en un archivo.
Las excepciones nunca deben comerse sin ningún tipo de nuevo lanzamiento, registro o incluso un comentario que explique por qué la excepción se consumió en silencio. En mis primeros años solía comer excepciones si sabía que era la única persona que trabajaba en el código. Por supuesto, cuando llegara el momento de revisar el código, me olvidaría de lo que realmente significaba esa excepción, y tendría que volver a probar todo para descubrirlo nuevamente.
Debido a que hay que terminar algo para una presentación, el jefe lo hace espontáneamente mañana y nada es peor que el programa que se cuelga o muestra un desagradable mensaje de error, mientras que cuando algo no funciona, siempre puede "hablar" de eso.
Después de la presentación, nadie mejorará esas partes "rápidas", por supuesto. ;-)
Incluso si es solo porque no se puede hacer nada con eso, aún sería bueno tener comentarios. Sin embargo, en las aplicaciones verticales / personalizadas en curso con un equipo estable, a veces esas sutilezas desaparecen si las razones son obvias en el código anterior.
Lo hago cuando una excepción no se puede manejar de manera efectiva, no debe afectar el funcionamiento normal de la aplicación, y / o podría representar una condición transitoria conocida, pero que tiene un impacto global mínimo.
Un ejemplo simple es el registro. No necesita que la aplicación explote solo porque no se guardará cierta información de registro en su almacén de respaldo.
Otro ejemplo podría ser donde tienes medios alternativos para manejar las situaciones, como este:
private Boolean SomeMethod() {
Boolean isSuccessful = false;
try {
// call API function that throws one of several exceptions on failure.
isSuccessful = true;
} catch(Exception) { }
return isSuccessful;
}
En el ejemplo anterior, es posible que no tenga una posición de retroceso, pero no desea que se filtre. Esto está bien para los métodos CORTOS donde el valor de retorno solo se establece en un estado exitoso como el último paso en el bloque de prueba.
Lamentablemente, hay muchas llamadas que generarán excepciones en lugar de simplemente devolver un código de error o una condición falsa en el resultado. También he visto llamadas que arrojan más excepciones de lo que sugiere su documentación, que es una de las razones por las que pondré un truco a su alrededor.
Lo he hecho cuando hay algo de código donde, independientemente de que falle, quiero que el programa continúe. Por ejemplo, abrí un archivo de registro opcional e independientemente de por qué no funcionó, quiero continuar con la ejecución del programa.
Sin embargo, no es un buen código, ya que puedes obtener excepciones como las excepciones a la memoria que ignorar no tiene ningún sentido, así que aunque lo haya hecho, aún no es la mejor idea y no admitiría hacerlo en público. (Ups ...)
Lo hice en programas que están destinados a ejecutarse una vez para convertir algunos datos o algo solo para cerrar el compilador. Sin embargo, esa es una situación completamente diferente a cualquier forma de código de producción.
Básicamente, no hay una buena razón para hacer esto aparte de la pereza.
Necesitaba hacer esto una vez, porque un viejo marco arrojaba excepciones sin sentido en casos raros, que no me importaba. Solo necesitaba que se ejecutara en los casos que funcionó.
Porque a veces el programador tiene más conocimiento que el compilador.
Por ejemplo, (en algún lenguaje ficticio donde la división por cero se considera un error):
try {
x = 1 / (a*a + 1);
} catch (DivisionByZeroException ex) {
// Cannot happen as a*a+1 is always positive
}
Como algunos lenguajes (por ejemplo, Java) requieren capturar algunas / muchas / todas las excepciones, el compilador puede quejarse mientras el programador sabe que no es posible. En ocasiones, la única forma de cerrar el compilador es tragar explícitamente la excepción.
En los idiomas en los que el compilador no se queja, la captura vacía generalmente no se escribe.
Editar: en la práctica, agregaría una afirmación (falsa) en el bloque catch. Si teóricamente algo no puede suceder, es un problema muy serio cuando sucede en la práctica;)
Edit2: Java solo requiere capturar excepciones marcadas. Reformulado mi declaración un poco.
Porque en algún momento incluso los buenos programadores cometen errores.
O eso o su opinión de un buen programador no es lo mismo que algunos (porque un buen programador habría dejado algún razonamiento detrás de por qué se tragó la excepción en lugar de algo que se haya hecho con la información).
Principalmente porque están muy cansados. La misma razón por la que olvidan hacer documentación alguna vez.
Sí, los uso bastante a menudo en el código de limpieza de excepción para no ocultar la excepción original.
Por ejemplo, si su controlador de captura está intentando deshacer una transacción y cerrar una conexión antes de volver a replantear la excepción, puede haber varias razones por las que la reversión / cierre en sí misma podría fallar (ya que usted está atrapado como resultado de la excepción original). Por lo tanto, es posible que desee envolver la limpieza en un bloque de prueba con un controlador de captura vacía, básicamente diciendo "si algo sale mal en la limpieza, eso está bien, ya que tenemos problemas más grandes que informar"
Sin pretender ser un buen programador, al menos puedo explicar por qué a veces atrapo e ignoro. Hay momentos en que lo que estoy trabajando tiene que ver con una excepción para compilar, pero no es el lugar apropiado para manejar la excepción. Por ejemplo, recientemente estuve trabajando en un programa que analiza una gran cantidad de XML como una de sus tareas. El analizador XML real abrió un archivo, que generó una excepción. Sin embargo, el analizador XML no era el mejor lugar para generar un diálogo informando al usuario de la mala elección del archivo. En lugar de desviarme de arrojar la excepción al lugar correcto y manejarla allí, silenciosamente atrapé la excepción, dejando un comentario y TODO (que mi IDE rastrea para mí). Esto me permitió compilar y probar sin realmente manejar la excepción. A veces, me olvidaré de ocuparme de TODO inmediatamente, y la excepción tragada se queda por un tiempo.
Sé que esta no es la mejor práctica de programación, y ciertamente no soy un programador experto, pero creo que este es un ejemplo de por qué alguien querría hacer esto.
Solo puedo pensar en dos tipos de casos en los que he tragado excepciones.
Al cerrar archivos o conexiones de bases de datos. Si recibo un error al final, ¿qué voy a hacer al respecto? Supongo que realmente debería escribir algún tipo de mensaje de error, pero si ya he leído los datos con éxito, parece superfluo. También parece un evento muy diferente que suceda.
Más justificado: manejo de errores interno. Si no intento escribir en el archivo de registro, ¿dónde voy a escribir el error que dice que no pude escribir un error? En algunos contextos podría tratar de escribir en la pantalla, pero dependiendo de la aplicación, eso podría ser imposible, por ejemplo, un trabajo en segundo plano sin pantalla; o simplemente confundir al usuario que no podrá hacer nada sobre el error de todos modos.
Otro cartel mencionó casos en los que usted sabe que la excepción es imposible, o al menos, que para que ocurra, tendría que haber problemas importantes con su compilador o entorno operativo, como si no hubiera agregado dos números correctamente, en cuyo caso quién dice la excepción es significativa?
Estoy totalmente de acuerdo en que en estos raros casos en los que es legítimo, debe incluir un comentario para explicar por qué la excepción es irrelevante o imposible. Pero luego, diría que cada vez que escriba código potencialmente críptico, debe incluir un comentario explicativo.
Nota al margen: ¿Por qué los programadores incluyen rutinariamente comentarios como "x = x + 1; // agregan 1 a x", como duh, nunca lo hubiera sacado sin el comentario, pero luego arrojan un código realmente críptico sin ¿explicación?
Addendum cuatro años después de mi publicación original
Por supuesto, la verdadera razón por la cual incluso los buenos programadores a veces tragan excepciones es: "Estoy tratando de hacer funcionar la lógica básica, no estoy seguro de qué hacer si ocurre esta excepción, no quiero meterme con eso en este momento , Estoy teniendo demasiados problemas con el flujo lógico normal, pero volveré a hacerlo más tarde ". Y luego se olvidan de volver a eso.
Por cierto, una mala razón que he escuchado de varias personas que generalmente considero que son buenos programadores es: "No quiero mostrar un mensaje de error críptico al usuario. Eso simplemente los confundiría. Mejor es tragar el error en silencio. " Esa es una mala razón porque, (a) Todavía puede escribir algo en un archivo de registro. Y (b) ¿Es un mensaje de error críptico realmente peor que darle al usuario la impresión de que la operación tuvo éxito cuando no lo hizo? Ahora el cliente piensa que su orden está siendo procesada cuando en realidad no lo es, o cree que la ambulancia está siendo enviada para ayudar a su hijo que está gritando de dolor pero que realmente el mensaje nunca se transmitió, etc. Incluso en los casos más triviales , una publicación suave en un foro no sucedió o algo así, coincidiría más bien con un mensaje críptico que al menos me da la idea de que algo salió mal y si me importa debería volver a intentarlo o llamar a alguien, solo diga " actualización completa "cuando realmente no es así".
Tal vez porque no atrapará, pero tiene que ser capaz de hacerlo de todos modos. Sin embargo, no puedo pensar en ninguna razón para evitar algún tipo de controlador.
Tropecé con este viejo hilo y me sorprendí al no ver la única razón justificable por la cual un buen código simplemente tragaba excepciones (con o sin comentarios).
Cuando escribe código utilizando librerías y códigos que están fuera de su control, con frecuencia hay situaciones en las que necesita llamar a un método para obtener algún valor que puede o no estar allí, o realizar una operación que no se permite en un punto determinado de hora. Si el método al que llama arroja una excepción si no puede devolver un resultado válido en lugar de proporcionar una alternativa de flujo de control, entonces se verá forzado a tragar la excepción como una señal de que el valor no está disponible o no se puede realizar la operación.
Los buenos programadores harán todos los esfuerzos posibles para evitar el uso de la gestión de excepciones para controlar el flujo de control, excepto en escenarios de fallas reales que probablemente salgan como un error. Sin embargo, los buenos programadores también reutilizan extensamente bibliotecas y marcos para la eficiencia, y entienden que reescribir las bibliotecas para proporcionar mejores alternativas (como los patrones de TryGetValue), no siempre es una opción.
Yo diría que un buen programador no haría eso sin explicarlo.
try
{
//Some code
}
catch(Exception e) {
// Explanation of why the exception is being swallowed.
}
Sin embargo, es muy poco probable que me trague silenciosamente la clase Exception
base, por cualquier razón. Al menos trataría de registrar el error.
Aquí hay un ejemplo concreto que acabo de encontrar hoy en mi proyecto. Esto es parte de una función de JavaScript que obtiene un XMLHttpRequest
compatible con el navegador.
var XMLHttp = null;
if( window.XMLHttpRequest ) {
try {
XMLHttp = new XMLHttpRequest();
} catch(e) {} // we''ve already checked that this is safe.
}
else if( window.ActiveXObject ) {
...
}
Como el try
está envuelto en un if...else if
eso comprueba el tipo de objeto que se creará, es seguro tragarse la excepción.
al menos, escríbelo en la ventana de salida
try
{ }
catch (exception ex)
{
System.Diagnostics.Debug.Writeline(" exception in method so and so : " ex.message);
}