Uso eficiente del bloque try / catch?

21

¿Deberían usarse los bloques catch para escribir la lógica, es decir, manejar el control de flujo, etc.? ¿O solo por lanzar excepciones? ¿Tiene efecto en la eficiencia o mantenibilidad del código?

¿Cuáles son los efectos secundarios (si los hay) de la lógica de escritura en el bloque catch?

EDITAR:

He visto una clase Java SDK en la que han escrito lógica dentro del bloque catch. Por ejemplo (fragmento tomado de la java.lang.Integerclase):

        try {
            result = Integer.valueOf(nm.substring(index), radix);
            result = negative ? new Integer(-result.intValue()) : result;
        } catch (NumberFormatException e) {
            String constant = negative ? new String("-" + nm.substring(index))
                                       : nm.substring(index);
            result = Integer.valueOf(constant, radix);
        }

EDIT2 :

Estaba pasando por un tutorial donde lo cuentan como una ventaja de escribir la lógica de casos excepcionales dentro de las excepciones:

Las excepciones le permiten escribir el flujo principal de su código y tratar los casos excepcionales en otros lugares.

¿Alguna pauta específica sobre cuándo escribir lógica en el bloque catch y cuándo no?

HashimR
fuente
12
@Coder, creo que generalizas un poco. Sobre todo porque con muchas de las API existentes no puedes evitarlo.
CodesInChaos
8
@Coder: en Java, las excepciones a menudo se usan como un mecanismo para señalar problemas que son parte del flujo de código legítimo. Por ejemplo, si intenta abrir un archivo y falla, la biblioteca de Java le dice que al lanzar una excepción, porque las API que conducen a abrir un archivo no tienen otro mecanismo para devolver un error.
JeremyP
44
@Coder depende. Creo que al hacer IO, o al analizar datos complejos, que casi siempre están formateados correctamente, lanzar una excepción para indicar un error hace que el código sea mucho más limpio.
CodesInChaos
1
@Coded Sí, la ejecución está ahí para que el método le diga que no pudo hacer lo que se le pidió. Pero las excepciones no se deben usar para el control de flujo, por lo que C # también tiene un método int. TryParse que no arroja.
Andy
1
@Coder: Eso es basura total. Las cosas que son excepcionales en un nivel de código pueden no ser excepcionales en otro, o puede que desee, por ejemplo, presentar o agregar información de error o depuración antes de continuar.
DeadMG

Respuestas:

46

El ejemplo que cita se debe al diseño deficiente de la API (no hay una forma limpia de verificar si un String es un número entero válido, excepto tratar de analizarlo y detectar la excepción).

A nivel técnico, lanzar e intentar / atrapar son construcciones de flujo de control que le permiten saltar la pila de llamadas, nada más y nada menos. Saltar la pila de llamadas conecta implícitamente el código que no está muy cerca en la fuente, lo que es malo para el mantenimiento . Por lo tanto, solo debe usarse cuando necesite hacer eso y las alternativas sean aún peores. El caso ampliamente aceptado donde las alternativas son peores es el manejo de errores (códigos de retorno especiales que deben verificarse y pasar manualmente cada nivel de la pila de llamadas).

Si tiene un caso en el que las alternativas son peores (y realmente las ha considerado cuidadosamente), entonces diría que usar tirar e intentar / atrapar para controlar el flujo está bien. El dogma no es un buen sustituto del juicio.

Michael Borgwardt
fuente
1
+1, buenos puntos hechos. Creo que algunos procesamientos pueden ser válidos en la captura, como la preparación de un mensaje de error y el registro de errores. La liberación de recursos caros como conexiones db y referencias COM (.NET) podría agregarse al bloque Finalmente.
NoChance
@EmmadKareem Pensé en liberar recursos, pero generalmente tienes que liberarlos de todos modos en algún momento, incluso sin una situación de error. Por lo tanto, puede repetir usted mismo. Preparar un mensaje de registro es, por supuesto, aceptable.
scarfridge
@MichaelBorgwardt Creo que el diseño de la API no es tan pobre (aunque podría ser mejor). Intentar analizar una cadena no válida es claramente una condición de error y, por lo tanto, es exactamente el "caso ampliamente aceptado" que mencionó. De acuerdo, un método para determinar si una cadena es sintácticamente correcta es útil (aquí es donde podría ser mejor). Sin embargo, no es posible obligar a todos a llamar a este método antes del análisis real y tenemos que manejar el error. Mezclar el resultado real y el código de error es incómodo, por lo que aún arrojaría la excepción. Pero tal vez una excepción en tiempo de ejecución.
scarfridge
3
+1 para esa última oración.
FrustratedWithFormsDesigner
2
@scarfridge: estoy completamente de acuerdo contigo :) La ausencia de un método isInteger (String) de retorno booleano es todo lo que quise decir con "diseño de API pobre"
Michael Borgwardt
9

Esto es algo que tiende a depender del lenguaje y el paradigma.

La mayor parte de mi trabajo se realiza en Java (y a veces en C ++). La tendencia es utilizar excepciones solo para condiciones excepcionales. Hay algunas preguntas en Stack Overflow sobre la sobrecarga de rendimiento de las excepciones en Java , y como puede ver, no es exactamente insignificante. También hay otras preocupaciones, como la legibilidad y la facilidad de mantenimiento del código. Cuando se usa correctamente, las excepciones pueden delegar el manejo de errores a los componentes apropiados.

Sin embargo, en Python, reina la idea de que es más fácil pedir perdón que permiso. En la comunidad de Python, esto se conoce como EAFP , que contrasta con el enfoque "mirar antes de saltar" ( LBYL ) en lenguajes de estilo C.

Thomas Owens
fuente
2
+1 por mencionar los gastos generales con excepciones. Hago .NET y (al menos en mi máquina) incluso podría sentir cuando una excepción está por suceder por la duración que toma en algunos casos en comparación con otras operaciones normales.
NoChance
2
@EmmadKareem Solo si tiene un depurador adjunto. Puede lanzar varios miles de ellos por segundo sin un depurador.
CodesInChaos
@CodeInChaos, tienes razón. ¿Cómo iba a saber, escribo un código tan limpio que no hay nada en tiempo de ejecución :)
NoChance
@EmmadKareem Dependiendo de cómo se escribe una aplicación de red, las fallas de conexión o protocolo se manejan con una excepción. Las excepciones deben ser razonablemente rápidas en dicha aplicación para evitar ataques DoS triviales.
CodesInChaos
@CodeInChaos, es bueno saberlo. Estaba bromeando en mi último comentario.
NoChance
9

Esa no es la mejor manera de pensar en bloques try / catch. La forma de pensar acerca de los bloques try / catch es la siguiente: ha ocurrido una falla, ¿cuál es el mejor lugar para lidiar con ESTA falla específica? Esa puede ser la siguiente línea de código, puede tener veinte niveles en la cadena de llamadas. Donde sea que esté, ahí es donde debería estar.

Lo que hace el bloque catch depende del error y de lo que puede hacer al respecto. A veces simplemente se puede ignorar (falla al eliminar un archivo de memoria virtual sin datos importantes), a veces se usará para establecer el valor de retorno de las funciones en verdadero o falso (método de análisis int de java), a veces saldrá del programa . Todo eso depende del error.

La parte importante a comprender es: catch block == Sé cómo lidiar con esto.

jmoreno
fuente
6

Los bloques de captura solo deben usarse para manejar excepciones, nada más y nunca para controlar el flujo. En cualquier situación en la que desee utilizar excepciones para administrar el flujo, será mejor que verifique las condiciones previas.

El flujo de manejo con excepciones es lento y semánticamente incorrecto.

Tom Squires
fuente
44
Yo sé lo que está diciendo, pero vale la pena señalar que todo lo que hace es excepciones flujo del programa mango - sólo que sólo se debe utilizar para controlar el flujo de error excepcional ...
Max
¿No hay pautas específicas para escribir lógica en el bloque catch y cuándo no?
HashimR
44
Los analizadores de números y los formateadores de texto de Java son ejemplos famosos de uso problemático de excepciones: la única forma razonable de verificar una condición previa (que una cadena representa un número analizable) es intentar analizarla, y si falla con una excepción, ¿cuáles son sus opciones? ¿en la práctica? Debe detectar la excepción y administrar el flujo con ella, sin importar que se considere semánticamente incorrecta.
Joonas Pulakka
@JoonasPulakka Creo que su comentario wrt Analizadores de números y Formateadores de texto califica como una respuesta de tamaño completo a esta pregunta
mosquito
5

¿Deberían usarse los bloques catch para escribir la lógica, es decir, manejar el control de flujo, etc.? ¿O solo por lanzar excepciones? ¿Tiene efecto en la eficiencia o mantenibilidad del código?

Primero, olvide la noción de que "las excepciones deben usarse para condiciones excepcionales". También deje de preocuparse por la eficiencia, hasta que tenga un código que funcione de manera inaceptable y haya medido para saber dónde radica el problema.

El código es más fácil de entender cuando las acciones siguen en una secuencia simple, sin condicionales. Las excepciones mejoran la capacidad de mantenimiento al eliminar la comprobación de errores del flujo normal. No importa si la ejecución sigue el flujo normal el 99.9% del tiempo o el 50% del tiempo o el 20% del tiempo.

Lanza una excepción cuando una función no puede devolver un valor, cuando un procedimiento no puede completar la acción esperada o cuando un constructor no puede producir un objeto utilizable. Esto permite a los programadores suponer que una función siempre devuelve un resultado utilizable, un procedimiento siempre completa la acción solicitada, un objeto construido siempre está en un estado utilizable.

El mayor problema que veo con el código de manejo de excepciones es que los programadores escriben bloques try / catch cuando no deberían. Por ejemplo, en la mayoría de las aplicaciones web, si una solicitud de base de datos arroja una excepción, no se puede hacer nada en el controlador. Una cláusula genérica de captura en el nivel más alto es suficiente. Entonces el código del controlador puede ignorar felizmente la posibilidad de que el disco se haya bloqueado o que la base de datos esté fuera de línea o lo que sea.

Kevin Cline
fuente
1

Los bloques de captura no deben usarse para escribir lógica de código. Deben usarse solo para manejar errores. Un ejemplo es (1) limpiar los recursos asignados, (2) imprimir un mensaje útil y (3) salir con gracia.

Es cierto que las excepciones pueden ser objeto de abuso y causar gotoefectos no deseados . Pero eso no los hace inútiles. Cuando se usan correctamente , pueden mejorar muchos aspectos de su código.

sakisk
fuente