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 throwinstrucción al final del catchbloque.

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#.

Consulte también