Compartir a través de


Uso de excepciones

En C#, los errores del programa en tiempo de ejecución se propagan a través del programa mediante un mecanismo denominado excepciones. Las excepciones las inicia el código que encuentra un error y las detecta el código que puede corregir dicho error. El entorno de ejecución .NET o el código de un programa pueden producir excepciones. Una vez que se lanza una excepción, esta se propaga por la pila de llamadas hasta que se encuentra una sentencia catch para la excepción. Las excepciones no detectadas se controlan mediante un controlador de excepciones genérico proporcionado por el sistema que muestra un cuadro de diálogo.

Las excepciones se representan mediante clases derivadas de Exception. Esta clase identifica el tipo de excepción y contiene propiedades que tienen detalles sobre la excepción. Lanzar una excepción implica crear una instancia de una clase derivada de excepciones, configurar opcionalmente las propiedades de la excepción y luego lanzar el objeto usando la palabra clave throw. Por ejemplo:

class CustomException : Exception
{
    public CustomException(string message)
    {
    }
}
private static void TestThrow()
{
    throw new CustomException("Custom exception in TestThrow()");
}

Después de que se lanza una excepción, el tiempo de ejecución comprueba la instrucción actual para ver si está dentro de un bloque try. Si es así, se comprueban los catch bloques asociados al try bloque para ver si pueden detectar la excepción. Catch los bloques suelen especificar tipos de excepción; si el tipo del catch bloque es el mismo tipo que la excepción o una clase base de la excepción, el catch bloque puede controlar el método . Por ejemplo:

try
{
    TestThrow();
}
catch (CustomException ex)
{
    System.Console.WriteLine(ex.ToString());
}

Si la instrucción que produce una excepción no está dentro de un try bloque o si el try bloque que lo incluye no tiene ningún bloque coincidente catch , el tiempo de ejecución comprueba el método de llamada para una try instrucción y catch bloques. El entorno runtime sigue hasta la pila de llamadas para buscar un bloque catch compatible. Una vez encontrado y ejecutado el catch bloque, el control se pasa a la siguiente instrucción después de ese catch bloque.

Una try instrucción puede contener más de un catch bloque. Se ejecuta la primera catch instrucción que puede controlar la excepción; se omiten las siguientes catch instrucciones, incluso si son compatibles. Ordene los bloques catch de más específicos (o más derivados) a menos específicos. Por ejemplo:

using System;
using System.IO;

namespace Exceptions
{
    public class CatchOrder
    {
        public static void Main()
        {
            try
            {
                using (var sw = new StreamWriter("./test.txt"))
                {
                    sw.WriteLine("Hello");
                }
            }
            // Put the more specific exceptions first.
            catch (DirectoryNotFoundException ex)
            {
                Console.WriteLine(ex);
            }
            catch (FileNotFoundException ex)
            {
                Console.WriteLine(ex);
            }
            // Put the least specific exception last.
            catch (IOException ex)
            {
                Console.WriteLine(ex);
            }
            Console.WriteLine("Done");
        }
    }
}

Antes de ejecutar el bloque catch, el tiempo de ejecución comprueba los bloques finally. Finally los bloques permiten al programador limpiar cualquier estado ambiguo que pueda haber sido dejado por un bloque abortado try o liberar los recursos externos (como identificadores de gráficos, conexiones de base de datos o flujos de archivos) sin esperar a que el recolector de basura en el tiempo de ejecución finalice los objetos. Por ejemplo:

static void TestFinally()
{
    FileStream? file = null;
    //Change the path to something that works on your machine.
    FileInfo fileInfo = new System.IO.FileInfo("./file.txt");

    try
    {
        file = fileInfo.OpenWrite();
        file.WriteByte(0xF);
    }
    finally
    {
        // Closing the file allows you to reopen it immediately - otherwise IOException is thrown.
        file?.Close();
    }

    try
    {
        file = fileInfo.OpenWrite();
        Console.WriteLine("OpenWrite() succeeded");
    }
    catch (IOException)
    {
        Console.WriteLine("OpenWrite() failed");
    }
}

Si WriteByte() lanzó una excepción, el código en el segundo bloque try que intenta volver a abrir el archivo fallará si no se llama a file.Close(), y el archivo permanecerá bloqueado. Dado que los bloques finally se ejecutan incluso si se produce una excepción, el bloque finally del ejemplo anterior permite que el archivo se cierre correctamente y ayuda a evitar errores.

Si no se encuentra ningún bloque compatible catch en la pila de llamadas después de iniciar una excepción, se produce una de estas tres cosas:

  • Si la excepción está dentro de un finalizador, se anula el finalizador y se llama al finalizador base, si existe.
  • Si la pila de llamadas contiene un constructor estático o un inicializador de campo estático, se lanza una TypeInitializationException, y la excepción original se asigna a la propiedad InnerException de la nueva excepción.
  • Si se llega al comienzo del subproceso, este finaliza.