Control de excepciones (Guía de programación de C#)
Los programadores de C# usan un bloque try para separar el código que podría verse afectado por una excepción. Los bloques catch asociados se usan para controlar las excepciones resultantes. Un bloque finally contiene código que se ejecuta independientemente de si se produce una excepción en el bloque try
, como la liberación de recursos asignados en el bloque try
. Los bloques try
requieren uno o varios bloques catch
asociados, un bloque finally
o ambos.
En los ejemplos siguientes se muestra una instrucción try-catch
, una instrucción try-finally
y una instrucción try-catch-finally
.
try
{
// Code to try goes here.
}
catch (SomeSpecificException ex)
{
// Code to handle the exception goes here.
// Only catch exceptions that you know how to handle.
// Never catch base class System.Exception without
// rethrowing it at the end of the catch block.
}
try
{
// Code to try goes here.
}
finally
{
// Code to execute after the try block goes here.
}
try
{
// Code to try goes here.
}
catch (SomeSpecificException ex)
{
// Code to handle the exception goes here.
}
finally
{
// Code to execute after the try (and possibly catch) blocks
// goes here.
}
Un bloque try
sin un bloque catch
o finally
produce un error del compilador.
Bloques catch
Los bloques catch
pueden especificar el tipo de excepción que quiere detectar. La especificación de tipo se denomina filtro de excepciones. El tipo de excepción se debe derivar de Exception. En general, no especifique Exception como el filtro de excepciones a menos que sepa cómo controlar todas las que puedan producirse en el bloque try
o que haya incluido una throw
instrucción al final del catch
bloque.
Se pueden encadenar juntos varios bloques catch
con distintas clases de excepciones. Los bloques catch
se evalúan de arriba abajo en el código, pero solo se ejecuta un bloque catch
para cada excepción que se produce. Se ejecuta el primer bloque catch
que especifica el tipo exacto o una clase base de la excepción producida. Si no hay ningún bloque catch
que especifique una clase de excepciones coincidente, se selecciona un bloque catch
que no tenga ningún tipo, si hay alguno en la instrucción. Es importante colocar primero los bloques catch
con las clases de excepción más específicas (es decir, las más derivadas).
Detecte excepciones cuando se cumplan las condiciones siguientes:
- Comprende bien el motivo por el que podría producirse la excepción y puede implementar una recuperación específica, por ejemplo, pedir al usuario que escriba un nuevo nombre de archivo cuando detecte un objeto FileNotFoundException.
- Puede crear y producir una nueva excepción más específica.
int GetInt(int[] array, int index) { try { return array[index]; } catch (IndexOutOfRangeException e) { throw new ArgumentOutOfRangeException( "Parameter index is out of range.", e); } }
- Quiere controlar parcialmente una excepción antes de pasarla para aumentar su control. En el ejemplo siguiente, se usa un bloque
catch
para agregar una entrada a un registro de errores antes de volver a producir la excepción.try { // Try to access a resource. } catch (UnauthorizedAccessException e) { // Call a custom error logging procedure. LogError(e); // Re-throw the error. throw; }
También puede especificar filtros de excepción para agregar una expresión booleana a una cláusula catch. Los filtros de excepción indican que una cláusula catch específica solo coincide cuando la condición es "true". En el ejemplo siguiente, ambas cláusulas catch usan la misma clase de excepción, pero se comprueba una condición adicional para crear un mensaje de error distinto:
int GetInt(int[] array, int index)
{
try
{
return array[index];
}
catch (IndexOutOfRangeException e) when (index < 0)
{
throw new ArgumentOutOfRangeException(
"Parameter index cannot be negative.", e);
}
catch (IndexOutOfRangeException e)
{
throw new ArgumentOutOfRangeException(
"Parameter index cannot be greater than the array size.", e);
}
}
Se puede usar un filtro de excepción que siempre devuelva false
para examinar todas las excepciones, pero no para procesarlas. Un uso habitual es registrar excepciones:
public class ExceptionFilter
{
public static void Main()
{
try
{
string? s = null;
Console.WriteLine(s.Length);
}
catch (Exception e) when (LogException(e))
{
}
Console.WriteLine("Exception must have been handled");
}
private static bool LogException(Exception e)
{
Console.WriteLine($"\tIn the log routine. Caught {e.GetType()}");
Console.WriteLine($"\tMessage: {e.Message}");
return false;
}
}
El método LogException
siempre devuelve false
; ninguna cláusula catch
que use este filtro de excepción coincide. La cláusula catch puede ser general, mediante el uso de System.Exception, y las cláusulas posteriores pueden procesar clases de excepción más específicas.
Bloques Finally
Los bloques finally
permiten limpiar las acciones que se realizan en un bloque try
. Si está presente, el bloque finally
se ejecuta en último lugar, después del bloque try
y de cualquier bloque catch
coincidente. Un bloque finally
siempre se ejecuta, independientemente de si se produce una excepción o si se encuentra un bloque catch
que coincida con el tipo de excepción.
Los bloques finally
pueden usarse para liberar recursos como secuencias de archivo, conexiones de base de datos y controladores de gráficos sin necesidad de esperar a que el recolector de elementos no utilizados en tiempo de ejecución finalice los objetos.
En el ejemplo siguiente, el bloque finally
se usa para cerrar un archivo que se abre en el bloque try
. Observe que se comprueba el estado del identificador de archivos antes de cerrar el archivo. Si el bloque try
no puede abrir el archivo, el manipulador de archivos sigue teniendo el valor null
, y el bloque finally
no intenta cerrarlo. En lugar de eso, si el archivo se abre correctamente en el bloque try
, el bloque finally
cierra el archivo abierto.
FileStream? file = null;
FileInfo fileinfo = new System.IO.FileInfo("./file.txt");
try
{
file = fileinfo.OpenWrite();
file.WriteByte(0xF);
}
finally
{
// Check for null because OpenWrite might have failed.
file?.Close();
}
Especificación del lenguaje C#
Para obtener más información, vea las secciones Excepciones y La instrucción try de la Especificación del lenguaje C#. La especificación del lenguaje es la fuente definitiva de la sintaxis y el uso de C#.