1. Introducción
En un mundo ideal, el código debería ser perfecto. Pero, lamentablemente, incluso el programador más cuidadoso algún día se va a topar con situaciones inesperadas: archivo no encontrado, el usuario metió una cadena en vez de un número, la red dejó de funcionar de repente, o algo volvió de la base de datos de forma rara.
Si no atrapas estos problemas — el programa simplemente "se cae", a veces con un mensaje de error misterioso y un stack trace (traza de llamadas). Eso no es lo que espera el usuario ni el futuro equipo de soporte. Hay que aprender a "atrapar" estos errores y reaccionar: terminar el programa de forma tranquila, mostrar un mensaje entendible o incluso arreglar la situación al vuelo.
try-catch — es la forma de decirle al compilador y al runtime: "Intenta ejecutar este trozo de código. Si pasa algo raro, no te asustes, ¡déjame a mí arreglarlo!"
2. Sintaxis básica de try-catch
La estructura general es súper simple:
try
{
// Aquí escribimos código peligroso (o potencialmente peligroso)
}
catch (ExceptionType variableName)
{
// Aquí escribimos qué hacer si ocurre una excepción del tipo ExceptionType
}
- El bloque try — es la "zona peligrosa". Ahí metemos las operaciones que pueden "explotar".
- El bloque catch — atrapa excepciones de un tipo concreto. Si dentro de try ocurre una excepción, el control salta al primer catch que encaje, y el código después del "lugar problemático" en try ya no se ejecuta.
Ejemplo: manejo simple de una excepción
Supón que nuestro mini-calculadora de las lecciones anteriores ahora puede dividir números. ¡Pero el usuario puede meter un cero! Mira cómo funciona sin manejo de errores:
int a = 10;
int b = 0;
int result = a / b; // ¡BOOM! System.DivideByZeroException
Console.WriteLine(result);
El programa terminará con el error DivideByZeroException:
Unhandled exception. System.DivideByZeroException: Attempted to divide by zero.
Ahora lo "arreglamos" usando try-catch:
int a = 10;
int b = 0;
try
{
int result = a / b; // operación peligrosa
Console.WriteLine("Resultado de la división: " + result);
}
catch (DivideByZeroException ex)
{
Console.WriteLine("¡Ups! No se puede dividir por cero: " + ex.Message);
}
Ahora el programa no "se cae". En la consola aparecerá un mensaje sobre la imposibilidad de dividir por cero:
¡Ups! No se puede dividir por cero: Attempted to divide by zero.
3. ¿Cómo funciona try-catch: paso a paso
En realidad, dentro de try puede haber tantas líneas de código como quieras. Si alguna acción dentro del bloque lanza una excepción, el control salta inmediatamente al catch más cercano que encaje. Todo lo que esté escrito después del error en el bloque try ya no se ejecuta.
Mira un ejemplo más grande:
try
{
Console.WriteLine("Empezamos...");
int[] numbers = { 1, 2, 3 };
Console.WriteLine(numbers[1]); // esto ok
Console.WriteLine(numbers[5]); // error: índice fuera del array
Console.WriteLine("¡Este mensaje ya no aparecerá!");
}
catch (IndexOutOfRangeException)
{
Console.WriteLine("Error: Intento de acceder a un elemento inexistente del array");
}
La salida en consola será así:
Empezamos...
2
Error: Intento de acceder a un elemento inexistente del array
Process finished with exit code 0.
¿Cómo funciona este programa?
- Se muestra el mensaje "Empezamos...".
- Luego el programa intenta mostrar el elemento numbers[1] — es 2.
- Cuando llega a numbers[5], ocurre el "accidente" y se lanza IndexOutOfRangeException.
- El programa salta al bloque catch, saltándose Console.WriteLine("¡Este mensaje ya no aparecerá!");
- En la consola aparece nuestro mensaje del catch.
4. ¿Se pueden manejar diferentes tipos de errores de forma diferente?
¡Sí! Podemos poner varios bloques catch para reaccionar de forma distinta a diferentes tipos de excepciones. Es muy útil: por ejemplo, si una acción tiene que ver con leer un archivo (puede lanzar FileNotFoundException), y otra — con dividir (puede lanzar DivideByZeroException).
try
{
// Tu código peligroso
}
catch (DivideByZeroException)
{
Console.WriteLine("Error de división por cero");
}
catch (IndexOutOfRangeException)
{
Console.WriteLine("Error: índice fuera del rango del array");
}
catch (Exception ex)
{
Console.WriteLine("Algún error inesperado: " + ex.Message);
}
Aquí el último bloque catch con tipo Exception — es el "cazador universal". Atrapa cualquier otra excepción que no haya encajado en los bloques anteriores. Pero ojo: si lo pones primero, los demás ya no se ejecutan nunca. Pon siempre los catch "amplios" al final.
5. ¿Cómo es el objeto de excepción?
Dentro del bloque catch podemos opcionalmente poner una variable — por ejemplo, catch (Exception ex). Ahí tienes toda la info sobre el error: mensaje, tipo de error, stack trace, excepciones anidadas.
Aquí va un ejemplo:
try
{
string? text = null;
Console.WriteLine(text.Length); // ¡Opa! NullReferenceException
}
catch (NullReferenceException ex)
{
Console.WriteLine("Excepción atrapada: " + ex.Message);
Console.WriteLine("Stack trace: " + ex.StackTrace);
}
Este enfoque es un salvavidas cuando depuras errores chungos: siempre podrás ver dónde y por qué tu programa "tropezó".
6. Atrapar el error y seguir trabajando
En aplicaciones reales, sobre todo de usuario, un error no es el fin del mundo. Si alguien mete una ruta de archivo mal, pasa. No es motivo para que el programa se reinicie en plan drama. Mejor le pedimos que lo intente otra vez.
El ejemplo de abajo muestra un enfoque simple pero efectivo: envolvemos la lectura del archivo en un bucle, y si algo falla, simplemente avisamos del error y pedimos que lo intente de nuevo. Sin dramas, sin pantallas rojas.
bool success = false;
while (!success)
{
Console.Write("Introduce el nombre del archivo: ");
string fileName = Console.ReadLine() ?? "";
try
{
string content = File.ReadAllText(fileName);
Console.WriteLine("¡Archivo leído con éxito!");
success = true; // ¡YEAH!
}
catch (FileNotFoundException)
{
Console.WriteLine("Error: archivo no encontrado. Intenta otra vez.");
}
}
La idea clave aquí no es solo atrapar la excepción, sino manejarla bien: no dejamos que el programa se caiga, sino que lo ponemos en modo "venga, inténtalo otra vez pero bien". El usuario recibe una pista clara, el programa — una segunda oportunidad, y tú — agradecimiento y respeto.
7. Errores típicos al trabajar con try-catch
Error nº1: Envolver todo el código en un solo try-catch universal.
Suena tentador — un try, un catch (Exception), y parece que "todo está protegido". Pero en realidad pierdes el control de la situación. No sabes dónde falló ni a qué error estás reaccionando. Al final, en vez de fiabilidad — pierdes toda la capacidad de diagnosticar.
Error nº2: Atrapar excepciones y no hacer nada con ellas.
Ejemplo de la vida real:
try
{
// algo potencialmente peligroso
}
catch
{
// silencio...
}
Este código literalmente dice: "Algo salió mal... bueno, da igual". Es peligroso: el programa puede seguir en un estado incorrecto y ni te enteras de que hubo un error. Al menos muestra un mensaje o registra el fallo. ¡Nada de
catch vacíos!
Error nº3: Atrapar un tipo de excepción demasiado general.
Si atrapas Exception, aunque sabes que puede ocurrir, por ejemplo, FormatException o FileNotFoundException, — te quitas la oportunidad de reaccionar bien a un problema concreto. Cuanto más preciso y concreto sea tu catch, más predecible y pro se comporta tu programa en situaciones raras.
8. Para qué NO se recomienda usar excepciones
A veces los novatos empiezan a usar excepciones... como si fueran una forma normal de controlar la lógica, por ejemplo:
try
{
if (x < 0) throw new Exception("x debe ser >= 0");
}
catch (Exception)
{
// rollback de estado, seguir trabajando...
}
¡No lo hagas! Las excepciones son una herramienta para situaciones inesperadas, raras (error al leer un archivo, fallo de red, etc.), no para comprobaciones normales de validez de datos de usuario. Para eso usa condicionales (if, else) y devuelve valores especiales si algo va mal.
GO TO FULL VERSION