En programación informática , existen varios mecanismos de lenguaje de programación para el manejo de excepciones . El término excepción se usa normalmente para denotar una estructura de datos que almacena información sobre una condición excepcional. Un mecanismo para transferir el control, o generar una excepción, se conoce como throw ; se dice que la excepción se ha lanzado . La ejecución se transfiere a un catch .
Uso
Los lenguajes de programación difieren sustancialmente en su noción de lo que es una excepción. Las excepciones pueden usarse para representar y manejar situaciones anormales, impredecibles y erróneas, pero también como estructuras de control de flujo para manejar situaciones normales. Por ejemplo, los iteradores de Python lanzan excepciones StopIteration para indicar que no hay más elementos producidos por el iterador. [ 1 ] Existe desacuerdo dentro de muchos lenguajes sobre lo que constituye un uso idiomático de las excepciones. Por ejemplo, Joshua Bloch afirma que las excepciones de Java solo deben usarse para situaciones excepcionales, [ 2 ] pero Kiniry observa que java.io.FileNotFoundExceptionla clase de Java no es en absoluto un evento excepcional. [ 3 ] De manera similar, Bjarne Stroustrup, autor de C++, afirma que las excepciones de C++ solo deben usarse para el manejo de errores, ya que para eso fueron diseñadas, [ 4 ] pero Kiniry observa que muchos lenguajes modernos como Ada, C++, Modula-3, ML y OCaml, Python y Ruby usan excepciones para el control de flujo. Algunos lenguajes como Eiffel, C#, Common Lisp y Modula-2 han hecho un esfuerzo concertado para restringir el uso de excepciones, aunque esto se hace a nivel social más que técnico. [ 3 ]
Historia
Los primeros compiladores de IBM Fortran tenían instrucciones para probar condiciones excepcionales. Estas incluían las instrucciones IF ACCUMULATOR OVERFLOW, IF QUOTIENT OVERFLOW, y IF DIVIDE CHECK. En aras de la independencia de la máquina, no se incluyeron en FORTRAN IV ni en el estándar Fortran 66. Sin embargo, desde Fortran 2003 es posible probar problemas numéricos mediante llamadas a funciones en el IEEE_EXCEPTIONSmódulo.
El manejo de excepciones de software continuó desarrollándose en las décadas de 1960 y 1970. LISP 1.5 (1958-1961) [ 5 ] permitió que las excepciones fueran generadas por la ERRORpseudofunción, de manera similar a los errores generados por el intérprete o compilador. Las excepciones eran capturadas por la ERRORSETpalabra clave, que devolvía NILen caso de un error, en lugar de terminar el programa o entrar al depurador . [ 6 ] PL/I introdujo su propia forma de manejo de excepciones alrededor de 1964, permitiendo que las interrupciones fueran manejadas con unidades ON. [ 7 ] MacLisp observó que ERRSETy ERRse usaban no solo para generar errores, sino también para el flujo de control no local, y por lo tanto agregó dos nuevas palabras clave, CATCHy THROW(junio de 1972). [ 8 ] El comportamiento de limpieza ahora generalmente llamado "finally" fue introducido en NIL (New Implementation of LISP) a mediados o finales de la década de 1970 como UNWIND-PROTECT. [ 9 ] Esto fue luego adoptado por Common Lisp . Contemporáneo a esto se encontraba dynamic-windScheme, que manejaba excepciones en cierres. Los primeros trabajos sobre el manejo estructurado de excepciones fueron Goodenough (1975a) y Goodenough (1975b) . [ 10 ] El manejo de excepciones fue posteriormente adoptado ampliamente por muchos lenguajes de programación a partir de la década de 1980.
Sintaxis
Muchos lenguajes de programación tienen soporte sintáctico incorporado para excepciones y manejo de excepciones. Esto incluye Ada , BlitzMax , C++ , C# , Clojure , COBOL , D , ECMAScript (por ejemplo, ActionScript , JavaScript ), Eiffel , Java , ML , Object Pascal (por ejemplo, Delphi , Free Pascal ), PowerBuilder , Objective-C , OCaml , Perl , [ 11 ] PHP (a partir de la versión 5), PL/I , PL/SQL , Prolog , Python , REALbasic , Ruby , Scala , Smalltalk , Tcl , Visual Prolog y la mayoría de los lenguajes .NET .
Excluyendo pequeñas diferencias sintácticas, solo se utilizan unos pocos estilos de manejo de excepciones. En el estilo más popular, una excepción se inicia mediante una instrucción especial ( throwo raise) con un objeto de excepción (por ejemplo, con Java u Object Pascal) o un valor de un tipo enumerado extensible especial (por ejemplo, con Ada o SML). El ámbito para los manejadores de excepciones comienza con una cláusula marcadora ( tryo el iniciador de bloque del lenguaje como begin) y termina al comienzo de la primera cláusula manejadora ( catch, except, rescue). Pueden seguir varias cláusulas manejadoras, y cada una puede especificar qué tipos de excepción maneja y qué nombre usa para el objeto de excepción. Como variación menor, algunos lenguajes usan una sola cláusula manejadora, que maneja internamente la clase de la excepción.
También es común una cláusula relacionada ( finallyo ensure) que se ejecuta independientemente de si se produjo una excepción o no, normalmente para liberar los recursos adquiridos dentro del cuerpo del bloque de manejo de excepciones. Cabe destacar que C++ no proporciona esta construcción, recomendando en su lugar la técnica de adquisición de recursos mediante inicialización (RAII), que libera recursos mediante destructores . [ 12 ] Según un artículo de 2008 de Westley Weimer y George Necula , la sintaxis de los bloques try... finallyen Java es un factor que contribuye a los defectos de software. Cuando un método necesita manejar la adquisición y liberación de 3 a 5 recursos, los programadores aparentemente no están dispuestos a anidar suficientes bloques debido a problemas de legibilidad, incluso cuando esta sería una solución correcta. Es posible usar un único bloque try... finallyincluso cuando se trata de múltiples recursos, pero eso requiere un uso correcto de valores centinela , que es otra fuente común de errores para este tipo de problema. [ 13 ] : 8:6–8:7
Python y Ruby también permiten una cláusula ( else) que se utiliza en caso de que no se haya producido ninguna excepción antes de que se alcanzara el final del ámbito del manejador.
En su conjunto, el código de manejo de excepciones podría verse así (en la sintaxis de manejo de excepciones al estilo Java ):
import java.io.IOException ; import java.util.Scanner ;try { Scanner stdin = new Scanner ( System . in ); String line = stdin . nextLine ();if ( line . length () == 0 ) { throw new IOException ( "¡La línea leída desde la consola estaba vacía!" ); }System.out.printf ( "Hola % s ! %n" , line ) ; System.out.println ( " La tarea se ejecutó correctamente." ) ; } catch ( IOException e ) { System.out.println ( " ¡Hola!" ) ; } catch ( Exception e ) { System.out.printf ( " Error : % s%n " , e.getMessage ( ) ) ; } finally { System.out.println ( " El programa está terminando . " ) ; }C no tiene manejo de excepciones try-catch, sino que usa códigos de retorno para la verificación de errores. Las setjmpfuncioneslongjmp de la biblioteca estándar se pueden usar para implementar el manejo try-catch mediante macros. [ 14 ]
Perl 5 utiliza die`for` throwy `try-catch`. Tiene módulos CPAN que ofrecen semántica try-catch. [ 15 ]eval{}if($@){}
Semántica de terminación y reanudación
Cuando se lanza una excepción, el programa busca hacia atrás en la pila de llamadas a funciones hasta encontrar un manejador de excepciones. Algunos lenguajes requieren que se desenrolle la pila a medida que avanza esta búsqueda. Es decir, si la función f, que contiene un manejador Hpara la excepción E, llama a la función g, que a su vez llama a la función h, y ocurre una excepción Een h, entonces las funciones hy gpueden terminarse, y Hen fmanejará E. Esto se denomina semántica de terminación. Alternativamente, los mecanismos de manejo de excepciones pueden no desenrollar la pila al entrar [ nota 1 ] en un manejador de excepciones, lo que le da al manejador de excepciones la opción de reiniciar el cálculo, reanudarlo o desenrollarlo. Esto permite que el programa continúe el cálculo exactamente en el mismo lugar donde ocurrió el error (por ejemplo, cuando un archivo que faltaba está disponible) o que implemente notificaciones, registros, consultas y variables fluidas sobre el mecanismo de manejo de excepciones (como se hace en Smalltalk). Permitir que el cálculo se reanude donde se interrumpió se denomina semántica de reanudación.
Existen argumentos teóricos y de diseño a favor de cualquiera de las dos decisiones. Las discusiones sobre la estandarización de C++ entre 1989 y 1991 dieron como resultado la decisión definitiva de utilizar la semántica de terminación en C++. [ 16 ] Bjarne Stroustrup cita una presentación de Jim Mitchell como un dato clave:
Jim había utilizado el manejo de excepciones en media docena de lenguajes durante un período de 20 años y fue uno de los primeros defensores de la semántica de reanudación como uno de los principales diseñadores e implementadores del sistema Cedar/Mesa de Xerox . Su mensaje era
- “La rescisión del contrato es preferible a la reincorporación; esto no es una cuestión de opinión, sino de años de experiencia. La reincorporación es tentadora, pero no válida.”
Respaldó esta afirmación con la experiencia de varios sistemas operativos. El ejemplo clave fue Cedar/Mesa: fue escrito por personas que apreciaban y utilizaban la reanudación, pero después de diez años de uso, solo quedaba un uso de la reanudación en el sistema de medio millón de líneas: una consulta de contexto. Dado que la reanudación no era realmente necesaria para dicha consulta, la eliminaron y encontraron un aumento significativo de velocidad en esa parte del sistema. En todos y cada uno de los casos en que se había utilizado la reanudación, esta se había convertido, a lo largo de los diez años, en un problema y había sido reemplazada por un diseño más apropiado. Básicamente, cada uso de la reanudación había representado un fallo en mantener separados los distintos niveles de abstracción. [ 10 ]
Entre los lenguajes de manejo de excepciones con reanudación se incluyen Common Lisp con su sistema de condiciones , PL/I, Dylan, R , [ 17 ] y Smalltalk . Sin embargo, la mayoría de los lenguajes de programación más recientes siguen el modelo de C++ y utilizan semántica de terminación.
En C++, std::uncaught_exceptions()se ofrece la opción de contar el número de excepciones en el hilo actual que se han lanzado/relanzado y que aún no han entrado en un catchbloque correspondiente. Antes de C++20 , se utilizaba otra función std::uncaught_exception()que determinaba si se estaba produciendo o no el desenrollado de la pila. [ 18 ]
Implementación del manejo de excepciones
La implementación del manejo de excepciones en los lenguajes de programación generalmente implica una cantidad considerable de soporte tanto de un generador de código como del sistema de tiempo de ejecución que acompaña a un compilador. (Fue la adición del manejo de excepciones a C++ lo que puso fin a la vida útil del compilador original de C++, Cfront . [ 19 ] ) Dos esquemas son los más comunes. El primero,El registro dinámico genera código que actualiza continuamente las estructuras sobre el estado del programa en términos de manejo de excepciones. [ 20 ] Normalmente, esto agrega un nuevo elemento aldiseño del marco de pilaque sabe qué manejadores están disponibles para la función o método asociado con ese marco; si se lanza una excepción, un puntero en el diseño dirige al entorno de ejecución al código del manejador apropiado. Este enfoque es compacto en términos de espacio, pero agrega sobrecarga de ejecución al entrar y salir del marco. Se usó comúnmente en muchas implementaciones de Ada, por ejemplo, donde la generación compleja y el soporte en tiempo de ejecución ya eran necesarios para muchas otras características del lenguaje.El Manejo Estructurado de Excepciones(SEH) de 32 bits de Microsoft utiliza este enfoque con una pila de excepciones separada. [ 21 ] El registro dinámico, al ser bastante sencillo de definir, es susceptible deprueba de corrección. [ 22 ]
El segundo esquema, y el implementado en muchos compiladores C++ de calidad de producción y en Microsoft SEH de 64 bits , es unEnfoque basado en tablas . Este enfoque crea tablas estáticas entiempo de compilaciónyde enlaceque relacionan rangos delcontador de programacon el estado del programa con respecto al manejo de excepciones. [ 23 ] Luego, si se lanza una excepción, el sistema de tiempo de ejecución busca la ubicación de la instrucción actual en las tablas y determina qué manejadores están en juego y qué se debe hacer. Este enfoque minimiza la sobrecarga ejecutiva en el caso de que no se lance una excepción. Esto ocurre a costa de algo de espacio, pero este espacio se puede asignar a secciones de datos especiales de solo lectura que no se cargan ni se reubican hasta que realmente se lanza una excepción. [ 24 ] La ubicación (en memoria) del código para manejar una excepción no necesita estar dentro (ni siquiera cerca) de la región de memoria donde se almacena el resto del código de la función. Por lo tanto, si se lanza una excepción, puede ocurrir una disminución del rendimiento, aproximadamente comparable a una llamada a función [ 25 ] , si el código necesario para manejar la excepción debe cargarse/almacenarse en caché. Sin embargo, este esquema tiene un costo de rendimiento mínimo si no se lanza ninguna excepción. Dado que las excepciones en C++ se supone que sonexcepcionales(es decir, poco comunes/raros), la frase "excepciones de costo cero" [ nota 2 ] se usa a veces para describir el manejo de excepciones en C++. Al igual quela identificación de tipo en tiempo de ejecuciónprincipio de sobrecarga cerode C++ya que la implementación del manejo de excepciones en tiempo de ejecución requiere una cantidad de memoria no nula para latabla de búsqueda. [ 26 ] Por esta razón, el manejo de excepciones (y RTTI) se puede deshabilitar en muchos compiladores de C++, lo que puede ser útil para sistemas con memoria muy limitada [ 26 ] (comolos sistemas embebidos). Este segundo enfoque también es superior en términos de lograrseguridad de subprocesos.
En comparación con C++, donde cualquier tipo puede ser lanzado y capturado, en Java solo los tipos que extienden java.lang.Throwablepueden ser lanzados y capturados, y java.lang.Throwabletiene dos descendientes directos: java.lang.Error(que indica un problema grave que un programa razonable no necesita capturar) y java.lang.Exception(cualquier otra condición que un programa razonable pueda querer capturar y manejar). java.lang.Errorse suele reservar para problemas extremadamente graves que están fuera del alcance del programa, como java.lang.OutOfMemoryError, java.lang.ThreadDeath, o java.lang.AssertionError.
También se han propuesto otros esquemas de definición e implementación. Para los lenguajes que admiten metaprogramación , se han propuesto enfoques que no implican ninguna sobrecarga (más allá del soporte ya existente para la reflexión ). [ 27 ]
Manejo de excepciones basado en el diseño por contrato
Una visión diferente de las excepciones se basa en los principios del diseño por contrato y está respaldada, en particular, por el lenguaje Eiffel . La idea es proporcionar una base más rigurosa para el manejo de excepciones definiendo con precisión qué es un comportamiento "normal" y "anormal". Específicamente, el enfoque se basa en dos conceptos:
- Fallo : la incapacidad de una operación para cumplir su contrato. Por ejemplo, una suma puede producir un desbordamiento aritmético (no cumple su contrato de calcular una buena aproximación a la suma matemática); o una rutina puede no cumplir su postcondición.
- Excepción : un evento anómalo que ocurre durante la ejecución de una rutina (dicha rutina es la " receptora " de la excepción). Este evento anómalo se produce por el fallo de una operación invocada por la rutina.
El "principio de manejo seguro de excepciones", introducido por Bertrand Meyer en su libro "Construcción de software orientado a objetos" , sostiene que solo existen dos maneras significativas en que una rutina puede reaccionar cuando ocurre una excepción:
- Fallo o "pánico organizado": La rutina corrige el estado del objeto restableciendo el invariante (esta es la parte "organizada") y luego falla (entra en pánico), lo que provoca una excepción en quien la llama (para que el evento anormal no se ignore).
- Reintentar: La rutina vuelve a intentar el algoritmo, normalmente después de cambiar algunos valores para que el siguiente intento tenga más posibilidades de éxito.
En particular, no está permitido simplemente ignorar una excepción; un bloque debe reintentarse y completarse con éxito, o bien propagar la excepción a quien lo llamó.
Aquí hay un ejemplo expresado en sintaxis Eiffel. Se asume que una rutina send_fastsuele ser la mejor manera de enviar un mensaje, pero puede fallar, provocando una excepción; en ese caso, el algoritmo utiliza a continuación send_slow, que fallará con menos frecuencia. Si send_slowfalla, la rutina senden su conjunto debería fallar, lo que provocará que quien la llama reciba una excepción.
enviar ( m : MENSAJE ) es -- Enviar m a través del enlace rápido, si es posible, de lo contrario a través del enlace lento. local tried_fast , tried_slow : BOOLEAN hacer si tried_fast entonces tried_slow := True enviar_lento ( m ) sino tried_fast := True enviar_fast ( m ) fin rescate si no tried_slow entonces reintentar fin finLas variables locales booleanas se inicializan a False al inicio. Si send_fastfalla, el cuerpo ( docláusula) se ejecutará de nuevo, lo que provocará la ejecución de send_slow. Si esta ejecución de send_slowfalla, la rescuecláusula se ejecutará hasta el final sin retry(sin elsecláusula en el final if), lo que provocará que la ejecución de la rutina en su conjunto falle.
Este enfoque tiene la ventaja de definir claramente qué son los casos "normales" y "anormales": un caso anormal, que provoca una excepción, es aquel en el que la rutina no puede cumplir su contrato. Define una distribución clara de roles: la docláusula (cuerpo normal) se encarga de lograr, o intentar lograr, el contrato de la rutina; la rescuecláusula se encarga de restablecer el contexto y reiniciar el proceso, si esto tiene posibilidades de éxito, pero no de realizar ningún cálculo real.
Aunque las excepciones en Eiffel tienen una filosofía bastante clara, Kiniry (2006) critica su implementación porque "las excepciones que forman parte de la definición del lenguaje se representan mediante valores ENTEROS, las excepciones definidas por el desarrollador mediante valores CADENA. [...] Además, como son valores básicos y no objetos, no tienen una semántica inherente más allá de la que se expresa en una rutina auxiliar que necesariamente no puede ser infalible debido a la sobrecarga de representación en efecto (por ejemplo, no se pueden diferenciar dos enteros del mismo valor)". [ 3 ]
C++26 añade soporte para contratos, que se utilizan de la siguiente manera. [ 28 ]
int f ( const int x ) pre ( x != 1 ) // una aserción de precondición post ( r : r == x && r != 2 ) // una aserción de postcondición; r nombra el objeto resultante de f { contract_assert ( x != 3 ); // una declaración de aserción return x ; }Excepciones no controladas
Las aplicaciones contemporáneas se enfrentan a numerosos desafíos de diseño al considerar estrategias de manejo de excepciones. Particularmente en las aplicaciones empresariales modernas, las excepciones a menudo deben traspasar los límites de los procesos y de las máquinas. Parte del diseño de una estrategia sólida de manejo de excepciones consiste en reconocer cuándo un proceso ha fallado hasta el punto en que la parte de software del proceso no puede manejarlo de manera eficiente. [ 29 ]
Si se lanza una excepción y no se captura (operacionalmente, se lanza una excepción cuando no se especifica ningún controlador aplicable), la excepción no capturada es manejada por el entorno de ejecución; la rutina que hace esto se llamamanejador de excepciones no capturadas . [ 30 ] [ 31 ] El comportamiento predeterminado más común es terminar el programa e imprimir unmensaje de erroren la consola, que generalmente incluye información de depuración como una representación en cadena de la excepción y elrastreo de la pila. [ 30 ] [ 32 ] [ 33 ] Esto a menudo se evita al tener un manejador de nivel superior (nivel de aplicación) (por ejemplo, en unbucle de eventos) que captura las excepciones antes de que lleguen al tiempo de ejecución. [ 30 ] [ 34 ]
Cabe señalar que, si bien una excepción no controlada puede provocar que el programa finalice de forma anormal (el programa puede no ser correcto si no se controla una excepción, en particular al no revertir transacciones parcialmente completadas o al no liberar recursos), el proceso finaliza normalmente (siempre que el entorno de ejecución funcione correctamente), ya que este (que controla la ejecución del programa) puede garantizar un cierre ordenado del proceso.
En un programa multihilo, una excepción no controlada en un hilo puede provocar la terminación únicamente de ese hilo, y no de todo el proceso (las excepciones no controladas en el manejador de nivel de hilo son capturadas por el manejador de nivel superior). Esto es especialmente importante para los servidores, donde, por ejemplo, un servlet (que se ejecuta en su propio hilo) puede terminar sin que el servidor en su conjunto se vea afectado.
Este manejador de excepciones no capturadas predeterminado puede ser sobrescrito, ya sea globalmente o por hilo, por ejemplo para proporcionar un registro alternativo o informes al usuario final de excepciones no capturadas, o para reiniciar hilos que terminan debido a una excepción no capturada. Por ejemplo, en Java esto se hace para un solo hilo a través de Thread.setUncaughtExceptionHandlery globalmente a través de Thread.setDefaultUncaughtExceptionHandler; en Python esto se hace modificando sys.excepthook.
Excepciones comprobadas
Java introdujo la noción de excepciones verificadas, [ 35 ] [ 36 ] que son clases especiales de excepciones. En Java, una excepción verificada es específicamente cualquier excepción java.lang.Throwableque no extienda java.lang.RuntimeExceptiono java.lang.Error. Las excepciones verificadas que un método puede generar deben formar parte de la firma del método . Por ejemplo, si un método puede lanzar una java.io.IOException, debe declarar este hecho explícitamente en la firma de su método. De no hacerlo, se genera un error de compilación. Esto se declararía de la siguiente manera (también con java.util.zip.DataFormatException):
import java.io.File ; import java.io.IOException ; import java.util.zip.DataFormatException ;// Indica que se pueden lanzar IOException y DataFormatException public void operateOnFile ( File f ) throws IOException , DataFormatException { // ... }Según Hanspeter Mössenböck, las excepciones verificadas son menos convenientes pero más robustas. [ 37 ] Las excepciones verificadas pueden, en tiempo de compilación , reducir la incidencia de excepciones no manejadas que surgen en tiempo de ejecución en una aplicación determinada.
Kiniry escribe que "Como sabe cualquier programador de Java, el volumen de try catchcódigo en una aplicación Java típica a veces es mayor que el código comparable necesario para la comprobación explícita de parámetros formales y valores de retorno en otros lenguajes que no tienen excepciones comprobadas. De hecho, el consenso general entre los programadores de Java en las trincheras es que lidiar con excepciones comprobadas es una tarea casi tan desagradable como escribir documentación. Por lo tanto, muchos programadores informan que "resenten" las excepciones comprobadas". [ 3 ] Martin Fowler ha escrito "...en general creo que las excepciones son buenas, pero las excepciones comprobadas de Java son más problemáticas de lo que valen". [ 38 ] Hasta 2006, ningún lenguaje de programación importante había seguido a Java en la adición de excepciones comprobadas. [ 38 ] Por ejemplo, C# no requiere ni permite la declaración de ninguna especificación de excepción, con lo siguiente publicado por Eric Gunnerson: [ 39 ] [ 3 ] [ 38 ]
"El análisis de programas pequeños lleva a la conclusión de que exigir especificaciones de excepciones podría aumentar tanto la productividad de los desarrolladores como la calidad del código, pero la experiencia con grandes proyectos de software sugiere un resultado diferente: una menor productividad y un aumento mínimo o nulo en la calidad del código."
Anders Hejlsberg describe dos preocupaciones con las excepciones verificadas: [ 40 ]
- Control de versiones: Un método puede declararse para lanzar excepciones
XyY. En una versión posterior del código, no se puede lanzar una excepciónZdesde el método, porque haría que el nuevo código fuera incompatible con los usos anteriores. Las excepciones verificadas requieren que quienes llaman al método agreguenZa su cláusula throws o manejen la excepción. Alternativamente,Zpuede representarse erróneamente como unXo unY. - Escalabilidad: En un diseño jerárquico, cada sistema puede tener varios subsistemas. Cada subsistema puede generar varias excepciones. Cada sistema padre debe gestionar las excepciones de todos los subsistemas inferiores, lo que resulta en un número exponencial de excepciones que deben manejarse. Las excepciones verificadas requieren que todas estas excepciones se gestionen explícitamente.
Para sortear estos problemas, Hejlsberg dice que los programadores recurren a eludir la característica usando una declaración. Otra elusión es usar un manejador (o incluso un ). [ 40 ] Esto se conoce como manejo de excepciones de captura general o manejo de excepciones Pokémon por la frase del programa "¡ Hazte con todos! ". [ 41 ] Los tutoriales de Java desaconsejan el manejo de excepciones de captura general ya que puede capturar excepciones "para las cuales el manejador no fue diseñado". [ 42 ] Otra elusión desaconsejada es hacer que todas las excepciones sean subclases de , [ 43 ] haciendo así que la excepción no sea verificada. Una solución recomendada es usar un manejador de captura general o una cláusula throws pero con una superclase específica de todas las excepciones potencialmente lanzadas en lugar de la superclase general . Otra solución recomendada es definir y declarar tipos de excepción que sean adecuados para el nivel de abstracción del método llamado [ 44 ] y mapear excepciones de nivel inferior a estos tipos usando el encadenamiento de excepciones .throwsExceptiontry{...}catch(Exceptione){...}try{...}catch(Throwablet){...}java.lang.RuntimeExceptionjava.lang.Throwable
Kotlin carece de excepciones verificadas, e incluso lanzar una excepción verificada de Java desde Kotlin no obligará al cliente a manejarla desde Java. Sin embargo, esto se puede habilitar mediante la @Throwsanotación, que emitirá los throwsmetadatos a la JVM. Por ejemplo, considere el siguiente código Kotlin:
import java.io.IOException@Throws ( IOException :: class ) fun readFile () { // ... throw IOException ( "¡Error al leer el archivo!" ) }Esto se traduciría aproximadamente a lo siguiente en la JVM:
import java.io.IOException ;public static final void readFile () throws IOException { // ... throw new IOException ( "¡Error al leer el archivo!" ); }Mecanismos similares
Las raíces de las excepciones verificadas se remontan a la noción de especificación de excepciones del lenguaje de programación CLU . [ 45 ] Una función podía generar solo las excepciones listadas en su tipo, pero cualquier excepción que se filtrara de las funciones llamadas se convertiría automáticamente en la única excepción en tiempo de ejecución, failureen lugar de generar un error en tiempo de compilación. [ 46 ] Posteriormente, Modula-3 tuvo una característica similar. [ 47 ] Estas características no incluyen la verificación en tiempo de compilación que es fundamental en el concepto de excepciones verificadas. [ 45 ]
Las primeras versiones del lenguaje de programación C++ incluían un mecanismo opcional similar a las excepciones verificadas, llamado especificación de excepciones . Por defecto, cualquier función podía lanzar cualquier excepción, pero esto podía limitarse mediante una throwcláusula (similar a la cláusula en Java) añadida a la firma de la función, que especificaba qué excepciones podía lanzar la función. Por ejemplo, este código era válido en C++03 :throws
#include <stdexcept>using std :: domain_error ; using std :: invalid_argument ;// Esto podría ser similar a la firma de Java // void performSomeOperation(int a, int b) throws InvalidArgumentException, ArithmeticException; void performSomeOperation ( int a , int b ) throw ( invalid_argument , domain_error ) { // ... }Las cláusulas de C++ throwpodían especificar cualquier número de tipos, incluso tipos primitivos y clases que no extendían std::exception(ya que C++ admite la generación de excepciones de cualquier tipo). Si no se especificaba ningún tipo en la throwcláusula, la función no generaría ninguna excepción.
Las especificaciones de excepción no se aplicaban en tiempo de compilación. Las violaciones resultaban en la llamada a la función de la biblioteca estándar std::unexpected()[ nota 3 ] . [ 48 ] Se podía dar una especificación de excepción vacía, lo que indicaba que la función no lanzaría ninguna excepción. Esto no se estableció como predeterminado cuando se agregó el manejo de excepciones al lenguaje porque habría requerido demasiada modificación del código existente, habría impedido la interacción con código escrito en otros lenguajes y habría tentado a los programadores a escribir demasiados manejadores a nivel local. [ 48 ] Sin embargo, el uso explícito de especificaciones de excepción vacías podría permitir a los compiladores de C++ realizar optimizaciones significativas de código y diseño de pila que están excluidas cuando el manejo de excepciones puede tener lugar en una función. [ 24 ] Algunos analistas consideraban que el uso adecuado de las especificaciones de excepción en C++ era difícil de lograr. [ 49 ] Este uso de especificaciones de excepciones se incluyó en C++98 y C++03 , se desaprobó en el estándar del lenguaje C++ de 2012 ( C++11 ), [ 50 ] y se eliminó del lenguaje en C++17 . Las cláusulas `throws` se reemplazaron por noexceptcláusulas `throws`. Una función que no lanzará ninguna excepción ahora se denotaría con la noexceptpalabra clave `throws`, y en su lugar se especifica que una función lanzará una excepción. Aunque las cláusulas `throws` se eliminan del lenguaje, escribir solo en la firma es legal y es equivalente a (ninguna excepción especificada por la cláusula denota que no puede lanzar una excepción), sin embargo, esto todavía se considera desaprobado. Para la transición de una base de código que usa cláusulas `throws`, la eliminada se puede redefinir como una macro. Esta es solo una solución temporal para permitir que el código compile, en lugar de una implementación real de excepciones verificadas. Usando esto, se puede imitar de manera similar las cláusulas de Java.noexcept(false)throwthrow()noexceptthrowthrowthrowthrows
// Si throw() está vacío, se expande a noexcept(true) (igual que noexcept), // de lo contrario se expande a noexcept(false). // Una instrucción throw sin paréntesis no se expandirá #define throw(...) noexcept(__VA_OPT__(!)true)importar std ;usando std :: runtime_error ;clase XException : public runtime_error {}; clase YException : public runtime_error {};// throw(Es...) se expandirá a noexcept(false) void performSomeOperation ( int x , int y ) throw ( XException , YException ) { if ( x > y ) { // throw sin corchete no se expandirá como una macro throw XException ( "x > y" ); } else if ( y > x ) { throw YException ( "y > x" ); } std :: println ( "x = y = {}" , x ); }// throw() se expandirá a noexcept(true) void willNotThrow ( int x ) throw () { std :: println ( "x = {}" , x ); }También se puede especificar que una función sea noexceptcondicional a que otra función sea noexcept, de la siguiente manera:
void mightThrow ();// El primer noexcept es la cláusula noexcept, el segundo es el operador noexcept que se evalúa a un valor booleano void f () noexcept ( noexcept ( mightThrow ()));Aunque C++ no tiene excepciones verificadas, se puede propagar el objeto lanzado hacia arriba en la pila dentro de un catchbloque escribiendo (sin especificar un objeto). Esto vuelve a lanzar el objeto capturado. Esto permite realizar operaciones dentro del bloque que lo captura, antes de permitir que el objeto continúe propagándose hacia arriba.throw;catch
Existe un analizador de excepciones no capturadas para el lenguaje de programación OCaml . [ 51 ] La herramienta informa el conjunto de excepciones generadas como una firma de tipo extendida. Pero, a diferencia de las excepciones verificadas, la herramienta no requiere anotaciones sintácticas y es externa (es decir, es posible compilar y ejecutar un programa sin haber verificado las excepciones).
En C++, también se puede realizar un manejo de excepciones tipo Pokémon. Al igual que en Java, C++ admite un bloque que captura cualquier objeto lanzado. Sin embargo, tiene la desventaja de no nombrar el objeto capturado, lo que significa que no se puede hacer referencia a él. Esto se debe a que en lenguajes como Java, solo se pueden lanzar excepciones de clases que extienden , mientras que en C++ se puede lanzar cualquier tipo (incluso primitivos), por lo que no existe una forma totalmente segura de almacenar una referencia al objeto capturado por un bloque catch-all.catch(Throwablet)catch(...)catch(...)java.lang.Throwable
importar std ;usando std :: exception ;// Capturar solo excepciones: try { // ... } catch ( const exception & e ) { // Capturar solo excepciones: std :: println ( "Se capturó una excepción: {}" , e . what ()); } catch (...) { // Capturar todos los objetos lanzados: std :: println ( "Se capturó un error desconocido" ); }El lenguaje Rust , en lugar de usar excepciones en su totalidad, representa las excepciones recuperables como tipos de resultado . [ 52 ] [ 53 ] Esto se representa como Result<T, E>(o expected<T, E>en C++). La ventaja de los tipos de resultado sobre las excepciones verificadas es que, si bien ambos tipos de resultado y excepciones verificadas obligan a los usuarios a manejar los errores de inmediato, también pueden representarse directamente como un tipo de retorno dentro del sistema de tipos del lenguaje , a diferencia de las excepciones verificadas donde la excepción declarada potencialmente lanzada es parte de la firma de la función pero no es directamente parte de su tipo de retorno.
Verificación dinámica de excepciones
El objetivo de las rutinas de manejo de excepciones es garantizar que el código pueda gestionar condiciones de error. Para comprobar la robustez de estas rutinas, es necesario someter el código a un amplio espectro de entradas no válidas o inesperadas, como las que se pueden generar mediante inyección de fallos de software y pruebas de mutación (también conocidas como pruebas de fuzzing ). Uno de los tipos de software más difíciles para escribir rutinas de manejo de excepciones es el software de protocolo, ya que una implementación robusta de protocolo debe estar preparada para recibir entradas que no cumplan con las especificaciones pertinentes.
Para garantizar que se pueda realizar un análisis de regresión significativo a lo largo del ciclo de vida del desarrollo de software , las pruebas de manejo de excepciones deben estar altamente automatizadas y los casos de prueba deben generarse de manera científica y repetible. Existen varios sistemas comerciales que realizan este tipo de pruebas.
En entornos de ejecución como Java o .NET , existen herramientas que se integran al motor de ejecución y, cada vez que se produce una excepción relevante, registran información de depuración presente en la memoria en el momento en que se generó ( valores de la pila de llamadas y del montón ). Estas herramientas se denominan herramientas de gestión automatizada de excepciones o de intercepción de errores y proporcionan información sobre la causa raíz de las excepciones.
excepciones asíncronas
Las excepciones asíncronas son eventos generados por un hilo separado o un proceso externo, como presionar Ctrl+C para interrumpir un programa, recibir una señal o enviar un mensaje disruptivo como "detener" o "suspender" desde otro hilo de ejecución . [ 54 ] [ 55 ] Mientras que las excepciones síncronas ocurren en una instrucción específica throw, las excepciones asíncronas pueden generarse en cualquier momento. Por consiguiente, el compilador no puede optimizar el manejo de excepciones asíncronas, ya que no puede probar la ausencia de excepciones asíncronas. También son difíciles de programar correctamente, ya que las excepciones asíncronas deben bloquearse durante las operaciones de limpieza para evitar fugas de recursos.
Los lenguajes de programación suelen evitar o restringir el manejo asíncrono de excepciones; por ejemplo, C++ prohíbe generar excepciones desde manejadores de señales, y Java ha desaconsejado el uso de su java.lang.ThreadDeatherror en Java 20, que se utilizaba para permitir que un hilo detuviera a otro. [ 56 ] Otra característica es un mecanismo semi-asíncrono que genera una excepción asíncrona solo durante ciertas operaciones del programa. Por ejemplo, el de Java java.lang.Thread::interrupt()solo afecta al hilo cuando este llama a una operación que lanza java.lang.InterruptedException. [ 57 ] La API POSIX similar tiene condiciones de carrera que hacen imposible su uso seguro. [ 58 ]pthread_cancel
Sistemas de acondicionamiento
Common Lisp , R , [ 59 ] Dylan y Smalltalk tienen un sistema de condiciones [ 60 ] (véase Sistema de condiciones de Common Lisp ) que abarca los sistemas de manejo de excepciones mencionados anteriormente. En esos lenguajes o entornos, la aparición de una condición (una "generalización de un error" según Kent Pitman ) implica una llamada a una función, y solo al final del manejador de excepciones se puede tomar la decisión de desenrollar la pila.
Las condiciones son una generalización de las excepciones. Cuando surge una condición, se busca y selecciona, en orden de pila, un manejador de condiciones apropiado para gestionarla. Las condiciones que no representan errores pueden quedar sin ser gestionadas; su único propósito puede ser proporcionar sugerencias o advertencias al usuario. [ 61 ]
Excepciones continuables
Esto se relaciona con el llamado modelo de reanudación del manejo de excepciones, en el que algunas excepciones se consideran continuables : se permite regresar a la expresión que generó la excepción, después de haber tomado medidas correctivas en el manejador. El sistema de condiciones se generaliza así: dentro del manejador de una condición no grave (también conocida como excepción continuable ), es posible saltar a puntos de reinicio predefinidos (también conocidos como reinicios ) que se encuentran entre la expresión que generó la excepción y el manejador de la condición. Los reinicios son funciones cerradas sobre un entorno léxico, lo que permite al programador reparar dicho entorno antes de salir completamente del manejador de la condición o desenrollar la pila, incluso parcialmente.
Un ejemplo es la condición ENDPAGE en PL/I; la unidad ON podría escribir las líneas de pie de página y las líneas de encabezado para la página siguiente, y luego continuar para reanudar la ejecución del código interrumpido.
Reinicia el mecanismo por separado de la política.
Además, el manejo de condiciones proporciona una separación entre mecanismo y política . Los reinicios ofrecen diversos mecanismos posibles para recuperarse de un error, pero no seleccionan el mecanismo apropiado en una situación dada. Esa es la función del manejador de condiciones, que (al estar ubicado en código de nivel superior) tiene acceso a una perspectiva más amplia.
Un ejemplo: supongamos que existe una función de biblioteca cuyo propósito es analizar una única entrada de un archivo syslog . ¿Qué debería hacer esta función si la entrada está mal formada? No hay una única respuesta correcta, ya que la misma biblioteca podría utilizarse en programas con diversos fines. En un navegador interactivo de archivos de registro, lo correcto sería devolver la entrada sin analizar para que el usuario pueda verla; pero en un programa automatizado de resumen de registros, lo correcto sería proporcionar valores nulos para los campos ilegibles, pero abortar con un error si hay demasiadas entradas mal formadas.
Es decir, la pregunta solo puede responderse en términos de los objetivos más amplios del programa, que no son conocidos por la función de la biblioteca de propósito general. Sin embargo, salir con un mensaje de error rara vez es la respuesta correcta. Por lo tanto, en lugar de simplemente salir con un error, la función puede establecer reinicios que ofrecen varias formas de continuar; por ejemplo, omitir la entrada del registro, proporcionar valores predeterminados o nulos para los campos ilegibles, solicitar al usuario los valores faltantes o desenrollar la pila y abortar el procesamiento con un mensaje de error. Los reinicios ofrecidos constituyen los mecanismos disponibles para recuperarse de un error; la selección del reinicio por parte del manejador de condiciones proporciona la política .
Crítica
El manejo de excepciones a menudo no se maneja correctamente en el software, especialmente cuando existen múltiples fuentes de excepciones; un análisis del flujo de datos de 5 millones de líneas de código Java encontró más de 1300 defectos en el manejo de excepciones. [ 13 ] Citando múltiples estudios previos de otros autores (1999–2004) y sus propios resultados, Weimer y Necula escribieron que un problema significativo con las excepciones es que "crean rutas de flujo de control ocultas que son difíciles de comprender para los programadores". [ 13 ] : 8:27 "Si bien try-catch-finally es conceptualmente simple, tiene la descripción de ejecución más complicada en la especificación del lenguaje [Gosling et al. 1996] y requiere cuatro niveles de "if" anidados en su descripción oficial en inglés. En resumen, contiene una gran cantidad de casos límite que los programadores a menudo pasan por alto." [ 13 ] : 8:13–8:14
Las excepciones, al ser flujos no estructurados, aumentan el riesgo de fugas de recursos (como escapar de una sección bloqueada por un mutex o que mantiene un archivo abierto temporalmente) o de estados inconsistentes. Existen diversas técnicas para la gestión de recursos en presencia de excepciones, la más común de las cuales combina el patrón `dispose` con algún tipo de protección de desenrollado (como una finallycláusula `unwind`), que libera automáticamente el recurso cuando el control sale de una sección de código.
En 1980, Tony Hoare describió el lenguaje de programación Ada como poseedor de «...una plétora de características y convenciones de notación, muchas de ellas innecesarias y algunas, como el manejo de excepciones, incluso peligrosas. [...] No permita que este lenguaje, en su estado actual, se utilice en aplicaciones donde la fiabilidad sea crítica [...]. El próximo cohete que se desvíe como resultado de un error en el lenguaje de programación podría no ser un cohete espacial de exploración en un viaje inofensivo a Venus: podría ser una ojiva nuclear explotando sobre una de nuestras propias ciudades». [ 62 ]
Los desarrolladores de Go creen que el modismo try-catch-finally oscurece el flujo de control , [ 63 ] e introdujeron el mecanismo similar a una excepción panic/ . [ 64 ] difiere de en que solo se puede llamar desde dentro de un bloque de código en una función, por lo que el manejador solo puede hacer limpieza y cambiar los valores de retorno de la función, y no puede devolver el control a un punto arbitrario dentro de la función. [ 65 ] El bloque en sí funciona de manera similar a una cláusula.recoverrecover()catchdeferdeferfinally
El lenguaje Rust no tiene excepciones. En su lugar, utiliza (un tipo de resultado ) para manejar errores en tiempo de ejecución, y para errores graves se utiliza la macro.Result<T,E>panic!()
Véase también
- Manejo automatizado de excepciones
- Continuación
- Programación defensiva
- Seguridad de excepciones
- Tipos de opciones y tipos de resultados : formas alternativas de manejar errores en la programación funcional sin excepciones.
Notas
- ↑ En PL/I, por ejemplo, una salida normal de un manejador de excepciones desenrolla la pila.
- ↑ El "costo de procesamiento es cero" solo si no se lanza ninguna excepción (aunque habrá un costo de memoria, ya que se necesita memoria para la tabla de búsqueda). Existe un costo (potencialmente significativo) si se lanza una excepción (es decir, si
throwse ejecuta). Implementar el manejo de excepciones también puede limitar las posibles optimizaciones del compilador que se pueden realizar. - ↑ Tenga en cuenta que esta función se eliminó en C++17 y el nombre se reintrodujo posteriormente en C++23 como unaclase de tipo de resultado
std::unexpected<T, E>.
Referencias
- ↑ "Excepciones integradas — Documentación de Python 3.10.4" . docs.python.org . Consultado el 17 de mayo de 2022 .
- ↑ Bloch, Joshua (2008). «Punto 57: Utilice excepciones solo para situaciones excepcionales» . Effective Java (2.ª ed.). Addison-Wesley. pág . 241. ISBN 978-0-321-35668-0.
- 1 2 3 4 5 Kiniry, JR (2006). "Excepciones en Java y Eiffel: Dos extremos en el diseño y la aplicación de excepciones". Temas avanzados en técnicas de manejo de excepciones (PDF) . Notas de clase en ciencias de la computación. Vol. 4119. págs. 288–300 . doi : 10.1007/11818502_16 . ISBN 978-3-540-37443-5. S2CID 33283674 .
- ↑ "Stroustrup: Preguntas frecuentes sobre estilo y técnica de C++" . www.stroustrup.com . Archivado del original el 2 de febrero de 2018. Consultado el 5 de mayo de 2018 .
- ↑ McCarthy, John (12 de febrero de 1979). "Historia de Lisp" . www-formal.stanford.edu . Consultado el 13 de enero de 2022 .
- ↑ McCarthy, John; Levin, Michael I.; Abrahams, Paul W.; Edwards, Daniel J.; Hart, Timothy P. (14 de julio de 1961). Manual del programador de LISP 1.5 (PDF) . Recuperado el 13 de enero de 2022 .
- ↑ "La instrucción ON" (PDF) . Sistema operativo IBM System/360, especificaciones del lenguaje PL/I (PDF) . IBM. Julio de 1966. pág. 120. C28-6571-3.
- ↑ Gabriel y Steele 2008 , pág. 3.
- ↑ White 1979 , pág. 194.
- 1 2 Stroustrup 1994 , pág. 392.
- ↑ "Excepciones - Documentación para el manejo de excepciones en Perl" . MetaCPAN .
- ↑ Stroustrup, Bjarne. "Preguntas frecuentes sobre estilo y técnica de C++" . www.stroustrup.com . Consultado el 12 de enero de 2022 .
- 1 2 3 4 Weimer, W; Necula, GC (2008). "Situaciones excepcionales y fiabilidad de los programas" (PDF) . ACM Transactions on Programming Languages and Systems . Vol. 30, n.º 2. Archivado (PDF) del original el 23 de septiembre de 2015.
- ↑ Roberts, Eric S. (21 de marzo de 1989). Implementación de excepciones en C (PDF) (Informe técnico). DEC Systems Research Center . SRC-RR-40 . Recuperado el 4 de enero de 2022 .
- ↑ Christiansen, Tom; Torkington, Nathan (2003). "10.12. Manejo de excepciones". Perl cookbook (2.ª ed.). Pekín: O'Reilly. ISBN 0-596-00313-7.
- ↑ Stroustrup 1994 , 16.6 Manejo de excepciones: reanudación frente a terminación, págs. 390–393.
- ↑ "R: Manejo y recuperación de condiciones" . search.r-project.org . Consultado el 5 de diciembre de 2022 .
- ↑ "std::uncaught_exception, std::uncaught_exceptions" . cppreference.com . cppreference . Consultado el 21 de noviembre de 2025 .
- ↑ Scott Meyers , El software C++ más importante... de todos los tiempos. Archivado el 28/04/2011 en Wayback Machine , 2006
- ↑ D. Cameron, P. Faust, D. Lenkov, M. Mehta, "Una implementación portátil del manejo de excepciones de C++", Actas de la Conferencia de C++ (agosto de 1992) USENIX .
- ↑ Peter Kleissner (14 de febrero de 2009). "Manejo de excepciones de Windows - Peter Kleissner" . Archivado del original el 14 de octubre de 2013. Consultado el 21 de noviembre de 2009 .Sección de manejo estructurado de excepciones basado en compilador
- ↑ Graham Hutton, Joel Wright, " Compilación correcta de excepciones archivadas el 11 de septiembre de 2014 en Wayback Machine ". Actas de la 7.ª Conferencia Internacional sobre Matemáticas de la Construcción de Programas , 2004.
- ↑ Lajoie, Josée (marzo-abril de 1994). "Manejo de excepciones: soporte del mecanismo de tiempo de ejecución". C++ Report . 6 (3).
- 1 2 Schilling, Jonathan L. (agosto de 1998). "Optimizing away C++ exception handling" . SIGPLAN Notices . 33 (8): 40– 47. doi : 10.1145/286385.286390 . S2CID 1522664 .
- ↑ "Mejores prácticas modernas de C++ para el manejo de excepciones y errores" . Microsoft . 8 de marzo de 2021. Consultado el 21 de marzo de 2022 .
- 1 2 Stroustrup, Bjarne (18 de noviembre de 2019). "Excepciones y alternativas de C++" (PDF) . Recuperado el 23 de marzo de 2022 .
- ↑ M. Hof, H. Mössenböck, P. Pirkelbauer, " Manejo de excepciones sin sobrecarga mediante metaprogramación Archivado el 3 de marzo de 2016 en Wayback Machine ", Actas de SOFSEM'97 , noviembre de 1997, Lecture Notes in Computer Science 1338 , págs. 423-431.
- ↑ "Contratos para C++" (PDF) . 13 de febrero de 2025.
- ↑ Todas las excepciones se manejan, Jim Wilcox, "Todas las excepciones se manejan" . 22 de febrero de 2008.
- 1 2 3 Biblioteca para desarrolladores de Mac , " Excepciones no capturadas archivadas el 4 de marzo de 2016 en Wayback Machine "
- ↑ MSDN , Evento AppDomain.UnhandledException archivado el 4 de marzo de 2016 en Wayback Machine
- ↑ El tutorial de Python , " 8. Errores y excepciones " Archivado el 1 de septiembre de 2015 en Wayback Machine .
- ↑ "Java Practices -> Proporcionar un manejador de excepciones no capturadas" . www.javapractices.com . Archivado del original el 9 de septiembre de 2016. Consultado el 5 de mayo de 2018 .
- ↑ PyMOTW (Módulo de Python de la semana), " Manejo de excepciones Archivado el 15/09/2015 en Wayback Machine "
- ↑ "Google Answers: El origen de las excepciones verificadas" . Archivado del original el 6 de agosto de 2011. Consultado el 15 de diciembre de 2011 .
- ↑ Especificación del lenguaje Java, capítulo 11.2. http://java.sun.com/docs/books/jls/third_edition/html/exceptions.html#11.2 Archivado el 8 de diciembre de 2006 en Wayback Machine
- ↑ Mössenböck, Hanspeter (25 de marzo de 2002). "C# avanzado: número variable de parámetros" (PDF) . Institut für Systemsoftware, Johannes Kepler Universität Linz, Fachbereich Informatik. pag. 32. Archivado (PDF) desde el original el 20 de septiembre de 2011 . Consultado el 5 de agosto de 2011 .
- 1 2 3 Eckel, Bruce (2006). Pensando en Java (4.ª ed.). Upper Saddle River, NJ: Prentice Hall. págs. 347–348 . ISBN 0-13-187248-6.
- ↑ Gunnerson, Eric (9 de noviembre de 2000). "C# y especificaciones de excepciones" . Archivado del original el 1 de enero de 2006.
- 1 2 Bill Venners; Bruce Eckel (18 de agosto de 2003). "El problema de las excepciones verificadas: una conversación con Anders Hejlsberg, parte II" . Recuperado el 4 de enero de 2022 .
- ↑ Juneau, Josh (31 de mayo de 2017). Java 9 Recipes: A Problem-Solution Approach . Apress. p. 226. ISBN 978-1-4842-1976-8.
- ↑ "Ventajas de las excepciones (Tutoriales de Java: Clases esenciales: Excepciones)" . Download.oracle.com. Archivado del original el 26/10/2011 . Consultado el 15/12/2011 .
- ↑ "Excepciones no controladas: la controversia (Tutoriales de Java: Clases esenciales: Excepciones)" . Download.oracle.com. Archivado del original el 17 de noviembre de 2011. Consultado el 15 de diciembre de 2011 .
- ↑ Bloch 2001:178 Bloch, Joshua (2001). Guía eficaz del lenguaje de programación Java . Addison-Wesley Professional. ISBN 978-0-201-31005-4.
- 1 2 "Bruce Eckel's MindView, Inc: ¿Necesita Java excepciones verificadas?" . Mindview.net. Archivado del original el 5 de abril de 2002. Recuperado el 15 de diciembre de 2011 .
- ↑ Liskov, BH; Snyder, A. (noviembre de 1979). "Gestión de excepciones en CLU" (PDF) . IEEE Transactions on Software Engineering . SE-5 (6): 546– 558. Bibcode : 1979ITSEn...5..546L . doi : 10.1109/TSE.1979.230191 . S2CID 15506879. Consultado el 19 de diciembre de 2021 .
- ↑ "Módulo-3 - Tipos de procedimiento" ..cs.columbia.edu. 8 de marzo de 1995. Archivado del original el 9 de mayo de 2008. Consultado el 15 de diciembre de 2011 .
- 1 2 Bjarne Stroustrup , El lenguaje de programación C++ , Tercera edición, Addison Wesley , 1997. ISBN 0-201-88954-4págs. 375-380.
- ↑ Reeves, JW (julio de 1996). "Diez directrices para las especificaciones de excepciones". C++ Report . 8 (7).
- ↑ Sutter, Herb (3 de marzo de 2010). "Informe de viaje: Reunión de estándares ISO C++ de marzo de 2010" . Archivado del original el 23 de marzo de 2010. Recuperado el 24 de marzo de 2010 .
- ↑ "OcamlExc - Un analizador de excepciones no capturadas para Objective Caml" . Caml.inria.fr. Archivado del original el 6 de agosto de 2011. Consultado el 15 de diciembre de 2011 .
- ↑ "std::result - Rust" . doc.rust-lang.org . Archivado del original el 09/10/2023 . Consultado el 09/10/2023 .
- ↑ "stdlib: Agregar módulo de resultados · rust-lang/rust@c1092fb" . github.com . 2011-10-29. Archivado del original el 2023-10-09 . Recuperado el 2023-10-09 .
- ↑ "Excepciones asíncronas en Haskell - Marlow, Jones, Moran (ResearchIndex)" . Citeseer.ist.psu.edu. Archivado del original el 23 de febrero de 2011. Consultado el 15 de diciembre de 2011 .
- ↑ Freund, Stephen N.; Mitchell, Mark P. Excepciones asíncronas seguras para Python (PDF) (Informe técnico) . Consultado el 4 de enero de 2022 .
- ↑ "Desuso de la primitiva de hilo de Java" . Java.sun.com. Archivado del original el 26 de abril de 2009. Consultado el 15 de diciembre de 2011 .
- ↑ "Interrupciones (Tutoriales de Java™ > Clases esenciales de Java > Concurrencia)" . docs.oracle.com . Consultado el 5 de enero de 2022 .
- ↑ Felker, Rich. "Cancelación de hilos y fugas de recursos" . ewontfix.com . Consultado el 5 de enero de 2022 .
- ↑ "R: Manejo y recuperación de condiciones" . search.r-project.org . Consultado el 25 de marzo de 2024 .
- ↑ De qué se tratan realmente las condiciones (excepciones) (24 de marzo de 2008). "De qué se tratan realmente las condiciones (excepciones)" . Danweinreb.org. Archivado del original el 1 de febrero de 2013. Consultado el 18 de septiembre de 2014 .
- ↑ "9.1 Conceptos del sistema de condiciones" . Franz.com. 25 de julio de 2022. Archivado del original el 7 de junio de 2024. Consultado el 7 de junio de 2024 .
- ↑ CAR Hoare. "La ropa vieja del emperador". Conferencia del Premio Turing de 1980.
- ↑ "Preguntas frecuentes" . Archivado del original el 3 de mayo de 2017. Consultado el 27 de abril de 2017. Creemos
que acoplar excepciones a una estructura de control, como en el patrón try-catch-finally, da como resultado un código complejo. Además, tiende a incentivar a los programadores a etiquetar como excepcionales demasiados errores comunes, como no poder abrir un archivo.
- ↑ Pánico y recuperación Archivado el 24/10/2013 en Wayback Machine , Ir a wiki
- ↑ Bendersky, Eli (8 de agosto de 2018). "Sobre los usos y abusos de los pánicos en Go" . Sitio web de Eli Bendersky . Recuperado el 5 de enero de 2022.
La limitación específica es que recover solo se puede llamar en un bloque de código defer, que no puede devolver el control a un punto arbitrario, sino que solo puede realizar limpiezas y ajustar los valores de retorno de la función.
Obras citadas
- Gabriel, Richard P.; Steele , Guy L. (2008). Un patrón de evolución del lenguaje (PDF) . LISP50: Celebrando el 50.º aniversario de Lisp. págs. 1–10 . doi : 10.1145/1529966.1529967 . ISBN 978-1-60558-383-9.
- Goodenough, John B. (1975a). Manejo estructurado de excepciones . Actas del 2.º simposio ACM SIGACT-SIGPLAN sobre principios de lenguajes de programación - POPL '75. págs. 204–224 . doi : 10.1145/512976.512997 .
- Goodenough, John B. (1975). "Manejo de excepciones: Problemas y una notación propuesta" (PDF) . Communications of the ACM . 18 (12): 683– 696. CiteSeerX 10.1.1.122.7791 . doi : 10.1145/361227.361230 . S2CID 12935051 .
- Stroustrup, Bjarne (1994). El diseño y la evolución de C++ (1.ª ed.). Reading, Mass.: Addison-Wesley. ISBN 0-201-54330-3.
- White, Jon L (mayo de 1979). NIL - Una perspectiva (PDF) . Actas de la Conferencia de Usuarios de Macsyma de 1979.
- Flujo de control
- Anomalías de software