Mejores prácticas para la administración de excepciones en Java o C# [cerrado]


Estoy atascado decidiendo cómo manejar las excepciones en mi aplicación.

Mucho si mis problemas con las excepciones provienen de 1) acceder a datos a través de un servicio remoto o 2) deserializar un objeto JSON. Desafortunadamente, no puedo garantizar el éxito de ninguna de estas tareas (cortar la conexión de red, objeto JSON malformado que está fuera de mi control).

Como resultado, si encuentro una excepción, simplemente la atrapo dentro de la función y devuelvo FALSE al llamador. Mi lógica es que todos los la persona que llama realmente se preocupa es si la tarea fue exitosa, no por qué no fue exitosa.

Aquí hay un código de ejemplo (en JAVA) de un método típico)

public boolean doSomething(Object p_somthingToDoOn)
{
    boolean result = false;

    try{
        // if dirty object then clean
        doactualStuffOnObject(p_jsonObject);

        //assume success (no exception thrown)
        result = true;
    }
    catch(Exception Ex)
    {
        //don't care about exceptions
        Ex.printStackTrace();
    }
    return result;
}

Creo que este enfoque está bien, pero tengo mucha curiosidad por saber cuáles son las mejores prácticas para la gestión de excepciones (¿debería realmente burbujear una excepción hasta el final de una pila de llamadas?).

En resumen de las preguntas clave:

  1. ¿Está bien simplemente capturar excepciones pero no burbujearlas o formalmente notificar al sistema (ya sea a través de un registro o una notificación al usuario)?
  2. ¿Cuáles son las mejores prácticas para las excepciones que no dan lugar a que todo requiera un bloque try/catch?

Seguir / Editar

Gracias por todos los comentarios, encontramos algunas excelentes fuentes sobre la gestión de excepciones en línea:

Parece que la gestión de excepciones es una de esas cosas que varían según el contexto. Pero lo más importante, uno debe ser consistente en la forma en que gestionan las excepciones dentro de un sistema.

Además, tenga cuidado con la podredumbre de código a través de intentos/capturas excesivas o no dar una excepción a su respeto (una excepción es advirtiendo al sistema, ¿qué más necesita ser advertido?).

Además, este es un bonito comentario de elección de m3rLinEz.

Tiendo a estar de acuerdo con Anders Hejlsberg y que la mayoría de las personas que llaman solo cuidado si la operación es exitosa o no.

De este comentario se desprenden algunas preguntas para pensar cuando se trata de excepciones:

  • ¿Qué sentido tiene esta excepción?
  • ¿Cómo tiene sentido manejarlo?
  • ¿Realmente le importa a la persona que llama la excepción o solo le importa si la llamada fue exitosa?
  • ¿Forzar a una persona que llama a gestionar una excepción potencial es agraciado?
  • ¿Estás siendo respetuoso con los idoms de la lengua?
    • ¿Realmente necesitas devolver una bandera de éxito como boolean? Devolver booleano (o un int) es más una mentalidad de C que una Java (en Java solo manejarías la excepción).
    • Seguir el error construcciones de gestión asociadas con el lenguaje:)!
Author: AtariPete, 2009-01-03

15 answers

Me parece extraño que quieras atrapar excepciones y convertirlas en códigos de error. ¿Por qué crees que la persona que llama preferiría códigos de error sobre excepciones cuando este último es el predeterminado tanto en Java como en C#?

En cuanto a sus preguntas:

  1. Solo debe capturar excepciones que realmente pueda manejar. Simplemente captura de excepciones no es lo correcto en la mayoría de los casos. Hay algunas excepciones (por ejemplo, excepciones de registro y clasificación entre hilos), pero incluso para esos casos que usted debe generalmente repensar las excepciones.
  2. Definitivamente no debe tener muchas declaraciones de intento / captura en su codificar. Una vez más, la idea es capturar solo excepciones que pueda manejar. Puede incluir un controlador de excepción superior para convertir cualquier excepciones en algo algo útil para el usuario final pero de lo contrario, no debe tratar de atrapar todas y cada una de las excepciones en en todos los lugares posibles.
 60
Author: Brian Rasmussen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-02 05:06:46

Esto depende de la aplicación y la situación. Si está creando un componente de biblioteca, debe agregar excepciones, aunque deben estar empaquetadas para que sean contextuales con su componente. Por ejemplo, si está creando una base de datos Xml y digamos que está utilizando el sistema de archivos para almacenar sus datos, y está utilizando los permisos del sistema de archivos para proteger los datos. Usted no querría burbujear una excepción FileIOAccessDenied ya que filtra su implementación. En su lugar, envolverías la excepción y lanzar un error AccessDenied. Esto es especialmente cierto si distribuye el componente a terceros.

En cuanto a si está bien tragar excepciones. Eso depende de tu sistema. Si su aplicación puede manejar los casos de falla y no hay beneficio de notificar al usuario por qué falló, siga adelante, aunque le recomiendo encarecidamente que registre la falla. Siempre me ha parecido frustrante ser llamado para ayudar a solucionar un problema y encontrar que se estaban tragando el excepción (o reemplazarla y lanzar una nueva sin establecer la excepción interna).

En general uso las siguientes reglas:

  1. En mis componentes y bibliotecas solo cojo una excepción si tengo la intención de manejarla o hacer algo basado en ella. O si quiero proporcionar información contextual adicional en una excepción.
  2. Utilizo una captura de prueba general en el punto de entrada de la aplicación, o el nivel más alto posible. Si llega una excepción, la registro y la dejo fallar. Idealmente, las excepciones nunca deberían llegar aquí.

Encuentro que el siguiente código es un olor:

try
{
    //do something
}
catch(Exception)
{
   throw;
}

Código como este no sirve de nada y no debe ser incluido.

 25
Author: JoshBerke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-03 18:51:46

Me gustaría recomendar otra buena fuente sobre el tema. Es una entrevista con los inventores de C# y Java, Anders Hejlsberg y James Gosling respectivamente, sobre el tema de la Excepción Comprobada de Java.

Fallos y excepciones

También Hay grandes recursos en la parte inferior de la página.

Tiendo a estar de acuerdo con Anders Hejlsberg y que la mayoría de las personas que llaman solo se preocupan si la operación es exitosa o no.

Bill Venners: indicado problemas de escalabilidad y control de versiones con respecto a las excepciones verificadas. ¿Podría aclarar lo que quiere decir con esos dos temas?

Anders Hejlsberg: Comencemos con versionado, porque los problemas son bastante fácil de ver allí. Digamos que crear un método foo que lo declare lanza excepciones A, B y C. En versión dos de foo, quiero añadir un un montón de características, y ahora foo podría lanzar excepción D. Es una ruptura cambiar para mí para agregar D a los tiros cláusula de ese método, porque el llamante existente de ese método casi con toda seguridad no manejar eso salvedad.

Agregar una nueva excepción a un lanzamiento cláusula en una nueva versión rompe cliente codificar. Es como agregar un método a un interfaz. Después de publicar un interfaz, es para todo práctico propósitos inmutables, porque cualquier aplicación de la misma podría tener la métodos que desea agregar en el siguiente versión. Así que tienes que crear un nuevo interfaz en su lugar. Similar con excepciones, usted tendría para crear un método completamente nuevo llamado foo2 que lanza más excepciones, o tendrías que coger la excepción D en el nuevo foo, y transformar la D en un A, B, o C.

Bill Venners : Pero no estás rompiendo su código en ese caso de todos modos, incluso en un idioma sin comprobar las excepciones? Si la nueva versión de foo va a lanzar una nueva excepción que los clientes deben pensar en manejo, ¿no es su código roto sólo por el el hecho de que no esperaban que ¿excepción cuando escribieron el código?

Anders Hejlsberg: No, porque en un lote de los casos, a la gente no le importa. Son no voy a manejar ninguno de estos salvedad. Hay un nivel inferior manejador de excepciones alrededor de su mensaje bucle. Ese manejador sólo va a abrir un diálogo que diga lo que pasó mal y continuar. Programador proteger su código escribiendo try por fin está en todas partes, así que volverán correctamente si se produce una excepción, pero en realidad no están interesados en manejando las excepciones.

La cláusula de tiros, al menos la forma está implementado en Java, no forzarte necesariamente a manejar la excepciones, pero si usted no maneja ellos, te obliga a reconocer exactamente qué excepciones podrían pasar mediante. Requiere que usted ya sea capturar excepciones declaradas o ponerlas en tu propia cláusula de lanzamientos. A trabajo alrededor de este requisito, la gente hace cosas ridículas. Por ejemplo, decorar cada método con, "lanza Salvedad."Que solo completamente derrota a la función, y acaba de hacer el programador escribe más gobbledy mugre. Eso no ayuda a nadie.

EDITAR: Se agregaron más detalles sobre la converstaion

 9
Author: Gant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-03 21:55:54

Las excepciones verificadas son un tema controvertido en general, y en Java en particular (más adelante trataré de encontrar algunos ejemplos para aquellos a favor y en contra de ellos).

Como reglas empíricas, el manejo de excepciones debe ser algo alrededor de estas pautas, sin ningún orden en particular:

  • Por el bien de la mantenibilidad, siempre registre excepciones para que cuando comience a ver errores, el registro lo ayude a indicarle el lugar donde probablemente haya comenzado su error. Nunca salir printStackTrace() o al igual que, lo más probable es que uno de sus usuarios obtenga uno de esos rastros de pila eventualmente, y tenga exactamente cero conocimiento en cuanto a qué hacer con él.
  • Captura excepciones que puedes manejar, y solo aquellas, y manejarlas, no solo las arrojes a la pila.
  • Siempre captura una clase de excepción específica, y generalmente nunca debe capturar el tipo Exception, es muy probable que se trague excepciones importantes.
  • Nunca (nunca) atrapar Error s!!, que significa: Nunca capture Throwable s ya que Error s son subclases de esta última. Error son problemas que probablemente nunca podrá manejar (por ejemplo, OutOfMemory u otros problemas de JVM)

Con respecto a su caso específico, asegúrese de que cualquier cliente que llame a su método recibirá el valor de retorno adecuado. Si algo falla, un método de retorno booleano puede devolver false, pero asegúrese de que los lugares que llama a ese método sean capaces de manejar eso.

 8
Author: Yuval Adam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-03 19:07:13

Solo debe capturar las excepciones con las que puede lidiar. Por ejemplo, si está lidiando con la lectura a través de una red y el tiempo de conexión se agota y obtiene una excepción, puede intentarlo de nuevo. Sin embargo, si está leyendo a través de una red y obtiene una excepción de IndexOutOfBounds, realmente no puede manejar eso porque no sabe (bueno, en este caso no sabrá) qué lo causó. Si vas a devolver false o -1 o null, asegúrate de que sea para excepciones específicas. No quiero una biblioteca que esté usando devolver un false en una lectura de red cuando la excepción lanzada es que el montón está fuera de memoria.

 5
Author: Malfist,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-03 18:57:45

Las excepciones son errores que no forman parte de la ejecución normal del programa. Dependiendo de lo que haga su programa y sus usos (es decir, un procesador de textos vs.un monitor cardíaco), querrá hacer cosas diferentes cuando encuentre una excepción. He trabajado con código que usa excepciones como parte de la ejecución normal y definitivamente es un olor a código.

Ex.

try
{
   sendMessage();

   if(message == success)
   {
       doStuff();
   }
   else if(message == failed)
   {
       throw;
   }
}
catch(Exception)
{
    logAndRecover();
}

Este código me hace vomitar. IMO no debe recuperarse de las excepciones a menos que sea un programa crítico. Si su lanzamiento excepciones entonces cosas malas están sucediendo.

 3
Author: Holograham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-03 19:14:17

Todo lo anterior parece razonable, y a menudo su lugar de trabajo puede tener una política. En nuestro lugar hemos definido los tipos de excepción: SystemException (sin marcar) y ApplicationException (marcado).

Hemos acordado que es poco probable que los SystemException sean recuperables y se manejarán una vez en la parte superior. Para proporcionar más contexto, nuestros SystemExceptions se amplían para indicar dónde ocurrieron, p.ej. RepositoryException, ServiceEception, etc.

ApplicationExceptions podría tener un significado comercial como InsufficientFundsException y debería ser manejado por el cliente codificar.

Con un ejemplo concreto, es difícil comentar sobre su implementación, pero nunca usaría códigos de retorno, son un problema de mantenimiento. Usted puede tragar una Excepción, pero usted necesita decidir por qué, y siempre registrar el evento y stacktrace. Por último, como su método no tiene otro procesamiento, es bastante redundante (excepto por la encapsulación?), por lo que doactualStuffOnObject(p_jsonObject); podría devolver un booleano!

 2
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-03 19:16:13

Después de pensar un poco y mirar su código me parece que simplemente está repensando la excepción como un booleano. Simplemente puedes dejar que el método pase esta excepción (ni siquiera tienes que atraparla) y lidiar con ella en la persona que llama, ya que ese es el lugar donde importa. Si la excepción hará que la persona que llama vuelva a intentar esta función, la persona que llama debe ser la que captura la excepción.

A veces puede suceder que la excepción que está encontrando no haga sentido a la persona que llama (es decir, es una excepción de red), en cuyo caso debe envolverlo en una excepción específica de dominio.

Si, por otro lado, la excepción señala un error irrecuperable en su programa (es decir, el resultado final de esta excepción será la terminación del programa), personalmente me gusta hacerlo explícito al atraparlo y lanzar una excepción de tiempo de ejecución.

 1
Author: wds,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-03 18:56:38

Si va a usar el patrón de código en su ejemplo, llámelo TryDoSomething y capture solo excepciones específicas.

También considere usar un Filtro de excepción al registrar excepciones con fines de diagnóstico. VB tiene soporte de idioma para filtros de excepción. El enlace al blog de Greggm tiene una implementación que se puede usar desde C#. Los filtros de excepción tienen mejores propiedades para la depurabilidad que catch y rethrow. Específicamente puede registrar el problema en el filtrar y dejar que la excepción continúe propagándose. Ese método permite que un depurador JIT (Justo a tiempo) tenga la pila original completa. Un rethrow corta la pila en el punto en que fue rethrown.

Los casos en los que TryXXXX tiene sentido son cuando se está envolviendo una función de terceros que arroja casos que no son verdaderamente excepcionales, o son simplemente difíciles de probar sin llamar a la función. Un ejemplo sería algo como:

// throws NumberNotHexidecimalException
int ParseHexidecimal(string numberToParse); 

bool TryParseHexidecimal(string numberToParse, out int parsedInt)
{
     try
     {
         parsedInt = ParseHexidecimal(numberToParse);
         return true;
     }
     catch(NumberNotHexidecimalException ex)
     {
         parsedInt = 0;
         return false;
     }
     catch(Exception ex)
     {
         // Implement the error policy for unexpected exceptions:
         // log a callstack, assert if a debugger is attached etc.
         LogRetailAssert(ex);
         // rethrow the exception
         // The downside is that a JIT debugger will have the next
         // line as the place that threw the exception, rather than
         // the original location further down the stack.
         throw;
         // A better practice is to use an exception filter here.
         // see the link to Exception Filter Inject above
         // http://code.msdn.microsoft.com/ExceptionFilterInjct
     }
}

Si utiliza un patrón como TryXXX o no es más una pregunta de estilo. La cuestión de atrapar todas las excepciones y tragarlas no es un problema de estilo. Asegúrese de que se permiten excepciones inesperadas para propagarse!

 1
Author: Steve Steiner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-03 21:40:59

Sugiero tomar sus señales de la biblioteca estándar para el lenguaje que está utilizando. No puedo hablar de C#, pero veamos Java.

Por ejemplo java.lang.reflejar.Array tiene un método estático set:

static void set(Object array, int index, Object value);

El camino C sería

static int set(Object array, int index, Object value);

... con el valor devuelto siendo un indicador de éxito. Pero ya no estás en el mundo C.

Una vez que acepta las excepciones, debe encontrar que hace que su código sea más simple y más claro, al mover su código de manejo de errores desde tu lógica central. Intenta tener muchas sentencias en un solo bloque try.

Como otros han señalado, debe ser lo más específico posible en el tipo de excepción que captura.

 1
Author: slim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-04 00:57:56

Si va a capturar una Excepción y devolver false, debe ser una excepción muy específica. No estás haciendo eso, estás atrapándolos a todos y devolviendo falsos. Si tengo una MyCarIsOnFireException quiero saberlo de inmediato! El resto de las excepciones puede que no me importe. Por lo tanto, debe tener una pila de manejadores de excepciones que digan "whoa whoa algo está mal aquí" para algunas excepciones (repensar, o atrapar y repensar una nueva excepción que explique mejor lo que sucedió) y solo devuelve false para los demás.

Si este es un producto que lanzará, debería registrar esas excepciones en algún lugar, lo ayudará a ajustar las cosas en el futuro.

Editar: En cuanto a la cuestión de envolver todo en un intento/captura, creo que la respuesta es sí. Las excepciones deben ser tan raras en su código que el código en el bloque catch se ejecuta tan raramente que no alcanza el rendimiento en absoluto. Una excepción debe ser un estado donde su máquina de estado se rompió y no sepa qué hacer. Al menos repensar una excepción que explica lo que estaba sucediendo en ese momento y tiene la excepción atrapada dentro de ella. "Excepción en el método doSomeStuff ()" no es muy útil para cualquiera que tenga que averiguar por qué se rompió mientras estás de vacaciones (o en un nuevo trabajo).

 0
Author: jcollum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-03 18:52:29

Mi estrategia:

Si la función original devuelve void la cambio a return bool. Si se produjo una excepción/error return false , si todo estaba bien return true .

Si la función debe devolver algo entonces cuando se produjo la excepción/error return null, de lo contrario el elemento retornable.

En lugar de bool se puede devolver una cadena que contenga la descripción del error.

En todos los casos antes de devolver cualquier cosa registrar el error.

 0
Author: Germstorm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-03 19:37:53

Algunas respuestas excelentes aquí. Me gustaría añadir, que si usted termina con algo como lo publicó, por lo menos imprimir más que el seguimiento de la pila. Di lo que estabas haciendo en ese momento, y Ex.getMessage (), para darle al desarrollador una oportunidad de lucha.

 0
Author: dj_segfault,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-03 21:34:51

Los bloques Try/catch forman un segundo conjunto de lógica incrustada sobre el primer conjunto (principal), como tal, son una gran manera de extraer código spaghetti ilegible y difícil de depurar.

Aún así, usados razonablemente hacen maravillas en legibilidad, pero solo debes seguir dos reglas simples:

  • Utilícelos (con moderación) en el nivel bajo para detectar problemas de manejo de bibliotecas y envíelos de nuevo al flujo lógico principal. La mayor parte del manejo de errores que queremos, debería venir del código sí, como parte de los datos en sí. ¿Por qué hacer condiciones especiales, si los datos devueltos no son especiales?

  • Use un manejador grande en el nivel superior para administrar cualquiera o todas las condiciones extrañas que surgen en el código que no se detectan en un nivel bajo. Haga algo útil con los errores (registros, reinicios, recuperaciones, etc.).

Aparte de estos dos tipos de manejo de errores, todo el resto del código en el medio debe estar libre y libre de código try / catch y error objeto. De esa manera, funciona de manera simple y como se espera, sin importar dónde lo use o qué haga con él.

Paul.

 0
Author: Paul W Homer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-04 00:14:15

Puedo llegar un poco tarde con la respuesta, pero el manejo de errores es algo que siempre podemos cambiar y evolucionar a lo largo del tiempo. Si quieres leer algo más sobre este tema escribí un post en mi nuevo blog al respecto. http://taoofdevelopment.wordpress.com

Feliz codificación.

 0
Author: GRGodoi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-01 21:42:34