gestion-excepciones-try-catch

Gestión de excepciones con Try Catch

La gestión de errores es una parte fundamental en el desarrollo de software que nos permite detectar, manejar y corregir situaciones inesperadas o excepcionales durante la ejecución de un programa.

Un error, que también llamaremos EXCEPCION, es una condición anormal o inesperada que interrumpe el flujo normal de ejecución de un programa.

La gestión de errores forma parte de las estructuras de control de flujo porque modifican el procedimiento normal del programa. Bien sea intencionadamente o no.

La aparición de una EXCEPCION puede ocurrir por distintas causas. Por ejemplo,

  • Error de sintaxis (es decir, has metido la pata en una parte del código)
  • Acceder a memoria no válida (como una variable no declarada, o salirte del largo de una colección)
  • Acceder a un recurso no disponible (un fichero, una base de datos, o una petición web…)
  • Lanzadas voluntariamente
  • Otras muchas causas…

Sea cual sea el motivo, lo importante es que ha ocurrido un error. Entonces el programa lanza un aviso de que ha ocurrido una anomalía. A este aviso generalmente lo llamamos EXCEPCION.

try-catch

Tus funciones repartiéndose la bomba 💣

Cuando ocurre una excepción, pueden pasar dos cosas,

  • Puedes capturarla, e intentar contener el error
  • Si no la capturas, se propaga a la función que llamó

Si la excepción sigue “subiendo” todas entre llamadas hasta llegar a la función principal, le saltará un error al usuario. En este caso, lo normal es que el programa se bloquee y deje de funcionar (de hecho lo normal es que cierre abruptamente, “modo boom”).

error-message-exception

Ejemplo de mensaje de error 💥

Lanzamiento y captura de excepciones

En la mayoría de los lenguajes de programación, las excepciones se lanzan cuando ocurre un error mediante la instrucción throw (o similar).

throw new Exception("¡Esta es una excepción lanzada manualmente!");

Estas excepciones pueden ser capturadas y manejadas por otras partes del código utilizando bloques try-catch o try-catch-finally.

try
{
    // Código que puede lanzar una excepción 💣
}
catch (Exception ex)
{
    // Manejo de la excepción, salta solo si ocurre un error 💥
}
finally
{
	// Bloque final opcional, se ejecuta siempre, ocurra error 💥 o no 👍
}
  • Bloque Try: El bloque try contiene el código que se desea ejecutar y que podría lanzar una excepción.
    Este código se ejecuta normalmente, pero si ocurre una excepción dentro del bloque try, el control del programa se transfiere inmediatamente al bloque catch (no se ejecutaría el código restante en el bloque try).

  • Bloque Catch: El bloque catch se ejecuta solo si ocurre una excepción dentro del bloque try.
    Aquí se especifica cómo manejar la excepción (como mostrar un mensaje de error, registrar la excepción o realizar acciones de recuperación).

  • Bloque Finally (opcional): El bloque finally se ejecuta siempre, ya sea que se produzca una excepción o no en el bloque try. Se utiliza para especificar código que debe ejecutarse después de la ejecución del bloque try, independientemente de si ocurre una excepción o no.
    Esto es útil para la limpieza de recursos (como cerrar archivos o conexiones de bases de datos, que deben realizarse independientemente de si se produce un error).

Ejemplo de excepciones en distintos lenguajes

Vamos a ver cómo usar un bloque try-catch para capturar excepciones y manejarlas de manera controlada en distintos lenguajes, y cómo lanzar una Excepcion manualmente con throw (o similar).

Para el ejemplo vamos a usar simplemente dividir por cero, que es una operación no permitida que genera un error. Es un forma muy sencilla de forzar lanzar la excepción para el ejemplo.

En C# las Excepciones se usan con el objeto Exception. Se capturan con un bloque try-catch-finally.

// Ejemplo en C#
try
{
    // Código que puede lanzar una excepción
    int resultado = 10 / 0; // División por cero 💥
}
catch (Exception ex)
{
    // Manejo de la excepción
    Console.WriteLine("Error: " + ex.Message);
}

Mientras que lanzar una excepción a mano se realizaría así.

throw new Exception("¡Esta es una excepción lanzada manualmente!");

En C++ la creación de un bloque try-catch es idéntica. En este caso las Excepciones forman parte del namespace std.

// Ejemplo en C++
try
{
    // Código que puede lanzar una excepción
    int resultado = 10 / 0; // División por cero 💥
}
catch (const std::exception& ex)
{
    // Manejo de la excepción
    std::cout << "Error: " << ex.what() << std::endl;
}

Mientras que así se lanzaría una Excepcion manualmente.

throw std::runtime_error("¡Esta es una excepción lanzada manualmente!");

JavaScript también usa la misma sintaxis de try-catch para captura de errores.

// Ejemplo en JavaScript
try {
    // Código que puede lanzar una excepción
    let resultado = 10 / 0; // División por cero 💥
} catch (error) {
    // Manejo de la excepción
    console.log("Error: " + error.message);
}

También es similar el lanzamiento de Excepciones manuales. En este caso la Excepción se llama Error.

 throw new Error("¡Esta es una excepción lanzada manualmente!");

Por último, Python como siempre tiene su sintaxis propia, y realiza el bloque con try-except

# Ejemplo en Python
try:
    # Código que puede lanzar una excepción
    resultado = 10 / 0  # División por cero 💥
except ZeroDivisionError as ex:
    # Manejo de la excepción
    print("Error:", ex)

Mientras que el lanzamiento de una Excepción manual se realiza con raise.

 raise Exception("¡Esta es una excepción lanzada manualmente!")

Como de costumbre, aparte de las pequeñas diferencias de sintaxis, la mayoría de lenguajes de programación incluyen gestión de Excepciones, con más similaridades que diferencias.

Buenas prácticas Consejos

No debéis abusar del uso del bloque try-catch. Su uso debe limitarse a situaciones realmente excepcionales o imprevisibles, y no como un mecanismo para controlar el flujo normal del programa.

Si en un proceso es previsible que ocurra un error, antes debemos comprobar las posibles causas de los errores. El bloque try-catch se queda únicamente para las cosas que no podemos comprobar.

Por ejemplo, imaginemos que quieres acceder a un fichero. La lectura de un fichero es un proceso que siempre es susceptible de generar Excepciones, porque es un recurso del sistema operativo que no controlamos nosotros.

try
{
	string contenido = File.ReadAllText(archivo);
	Console.WriteLine("Contenido del archivo: " + contenido);
}
catch (Exception ex)
{
	Console.WriteLine("Error durante la lectura del archivo: " + ex.Message);
}

Una posible causa es que el fichero no exista. Eso no es una Excepcion, es un motivo claro, evidente y conocido, de que no podamos leerlo. Por tanto, lo limpio es que hagamos esa comprobación previamente.

if (File.Exists(archivo))
{
	try
	{
		string contenido = File.ReadAllText(archivo);
		Console.WriteLine("Contenido del archivo: " + contenido);
	}
	catch (Exception ex)
	{
		Console.WriteLine("Error durante la lectura del archivo: " + ex.Message);
	}
}
else
{
	Console.WriteLine("El archivo no existe.");
}

Únicamente dejamos el try-catch para la lectura del fichero. Que puede ser susceptible de dar un error, incluso aunque el fichero exista. Esto ya es un imponderable o situación que no podemos preveer, y aquí es donde tiene sentido envolverlo en un try-catch.

Esto es así, en primer lugar, por limpieza. Pero, por otro lado, porque el try-catch tiene un impacto significativo en el rendimiento del programa. Así que únicamente debemos usarlo para lo que es, gestión de errores imprevistos. y no para todo.