try-catch (Referencia de C#)

La instrucción try-catch consta de un bloque try seguido de una o más cláusulas catch, las cuales especifican controladores para diferentes excepciones.

Comentarios

Cuando se produce una excepción, Common Language Runtime (CLR) busca la instrucción catch que controla esta excepción. Si el método que se está ejecutando actualmente no contiene un bloque catch de este tipo, CLR examina el método que llamó al método actual y así sucesivamente en la pila de llamadas. Si no se encuentra ningún bloque catch, CLR muestra al usuario un mensaje de excepción no controlada y detiene la ejecución del programa.

El bloque try contiene el código protegido que puede causar la excepción. Este bloque se ejecuta hasta que se produce una excepción o hasta completarse satisfactoriamente. Por ejemplo, el siguiente intento de convertir un objeto null provoca la excepción NullReferenceException:

object o2 = null;
try
{
    int i2 = (int)o2;   // Error
}

Aunque la cláusula catch puede utilizarse sin argumentos para detectar cualquier tipo de excepción, no se recomienda este uso. En general, sólo debe detectar las excepciones de las que sepa cómo recuperarse. Por lo tanto, debe especificar siempre un argumento de objeto derivado de Exception. Por ejemplo:

catch (InvalidCastException e) 
{
}

Es posible utilizar más de una cláusula catch específica en la misma instrucción try-catch. En este caso, el orden de las cláusulas catch es importante, ya que las cláusulas catch se examinan por orden. Las excepciones más específicas se capturan antes que las menos específicas. El compilador genera un error si ordena los bloques catch de forma que nunca se alcance un bloque posterior.

Se puede utilizar una instrucción throw en un bloque catch para volver a producir la excepción capturada por la instrucción catch. En el ejemplo siguiente se extrae información de origen de una excepción IOException y, a continuación, se inicia la excepción en el método primario.

catch (FileNotFoundException e)
{
    // FileNotFoundExceptions are handled here.
}
catch (IOException e)
{
    // Extract some information from this exception, and then 
    // throw it to the parent method.
    if (e.Source != null)
        Console.WriteLine("IOException source: {0}", e.Source);
    throw;
}

Puede detectar una excepción y producir otra distinta. Al hacerlo, especifique la excepción que se detectó como excepción interna, como se muestra en el ejemplo siguiente.

catch (InvalidCastException e) 
{
    // Perform some action here, and then throw a new exception.
    throw new YourCustomException("Put your error message here.", e);
}

También puede volver a producir una excepción cuando una condición especificada es true, como se muestra en el ejemplo siguiente.

catch (InvalidCastException e)
{
    if (e.Data == null)
    {
        throw;
    }
    else
    {
        // Take some action.
    }
 }

Desde dentro de un bloque try, inicialice únicamente las variables que se declaran. De lo contrario, puede producirse una excepción antes de que completarse la ejecución del bloque. Por ejemplo, en el siguiente código, la variable n se inicializa dentro del bloque try. Al intentar usar esta variable fuera del bloque try en la instrucción Write(n), se generará un error del compilador.

static void Main() 
{
    int n;
    try 
    {
        // Do not initialize this variable here.
        n = 123;
    }
    catch
    {
    }
    // Error: Use of unassigned local variable 'n'.
    Console.Write(n);
}

Para obtener más información sobre catch, vea try-catch-finally.

Excepciones en los métodos Async

Un método async se marca mediante un modificador async y normalmente contiene uno o más espera expresiones o instrucciones. Una expresión de aguardar aplica el operador espera a Task o a Task. Una expresión await no puede aparecer en un bloque catch o un bloque finally .

Cuando el control alcance await en el método async, el progreso en el método se suspende hasta que la tarea aguardada complete. Cuando finaliza la tarea, la ejecución se puede reanudar en el método. Para obtener más información, vea Programación asincrónica con Async y Await (C# y Visual Basic) y Flujo de control en programas asincrónicos (C# y Visual Basic).

La tarea completada a la que se aplica await podría ser con errores debido a una excepción no controlada en el método que devuelve la tarea. Aguardar la tarea produce una excepción. Una tarea también puede finalizar en un estado cancelado si el proceso asincrónico que la devuelve se cancela. Aguardar una tarea cancelada produce OperationCanceledException. Para obtener más información sobre cómo cancelar un proceso asincrónico, vea Ajustar una aplicación asincrónica (C# y Visual Basic).

Para detectar la excepción, aguardar la tarea en un bloque try , y detectar la excepción en el bloque asociado catch . Para obtener un ejemplo, vea la sección “ejemplo”.

Una tarea puede estar con errores porque varias excepciones se produjeron en el método aguardado async. Por ejemplo, la tarea puede ser el resultado de una llamada a Task.WhenAll. Cuando se espera por tarea, solo una de las excepciones se detecta, y no puede predecir qué excepción se detectar. Para obtener un ejemplo, vea la sección “ejemplo”.

Ejemplo

En el ejemplo siguiente, el bloque try contiene una llamada al método ProcessString que puede producir una excepción. La cláusula catch contiene el controlador de excepciones, el cual simplemente muestra un mensaje en la pantalla. Cuando se realiza la llamada a la instrucción throw desde dentro de MyMethod, el sistema busca la instrucción catch y muestra el mensaje Exception caught.

    class TryFinallyTest
{
    static void ProcessString(string s)
    {
        if (s == null)
        {
            throw new ArgumentNullException();
        }
    }

    static void Main()
    {
        string s = null; // For demonstration purposes. 

        try
        {            
            ProcessString(s);
        }

        catch (Exception e)
        {
            Console.WriteLine("{0} Exception caught.", e);
        }
    }
}
    /*
    Output:
    System.ArgumentNullException: Value cannot be null.
       at TryFinallyTest.Main() Exception caught.
     * */

En el ejemplo siguiente, se utilizan dos bloques catch, y se detecta la excepción más específica, que es la primera.

Para detectar la menos excepción concreta, puede reemplazar la instrucción throw en ProcessString con la siguiente instrucción: throw new Exception().

Si coloca el bloque catch menos- concreto primero en el ejemplo, el mensaje de error siguiente: A previous catch clause already catches all exceptions of this or a super type ('System.Exception').

class ThrowTest3
{
    static void ProcessString(string s)
    {
        if (s == null)
        {
            throw new ArgumentNullException();
        }
    }

    static void Main()
    {
        try
        {
            string s = null;
            ProcessString(s);
        }
        // Most specific: 
        catch (ArgumentNullException e)
        {
            Console.WriteLine("{0} First exception caught.", e);
        }
        // Least specific: 
        catch (Exception e)
        {
            Console.WriteLine("{0} Second exception caught.", e);
        }
    }
}
/*
 Output:
 System.ArgumentNullException: Value cannot be null.
 at Test.ThrowTest3.ProcessString(String s) ... First exception caught.
*/

El ejemplo siguiente se muestra el control de excepciones para los métodos async. Para detectar una excepción que una tarea async produce, coloque la expresión await en un bloque try , y detecte la excepción en un bloque catch .

Quite los comentarios de la línea throw new Exception en el ejemplo para mostrar el control de excepciones. La propiedad IsFaulted de la tarea se establece en True, la propiedad Exception.InnerException de la tarea se establece en la excepción, la excepción se detecta en el bloque catch .

Quite los comentarios de la línea throw new OperationCancelledException para mostrar qué ocurre al proceso anulan asincrónico. La propiedad IsCanceled de la tarea se establece en true, y la excepción se detecta en el bloque catch . En algunas condiciones que no se aplican a este ejemplo, la propiedad IsFaulted de la tarea se establece en true y IsCanceled se establece en false.

        public async Task DoSomethingAsync()
        {
            Task<string> theTask = DelayAsync();

            try
            {
                string result = await theTask;
                Debug.WriteLine("Result: " + result);
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception Message: " + ex.Message);
            }
            Debug.WriteLine("Task IsCanceled: " + theTask.IsCanceled);
            Debug.WriteLine("Task IsFaulted:  " + theTask.IsFaulted);
            if (theTask.Exception != null)
            {
                Debug.WriteLine("Task Exception Message: "
                    + theTask.Exception.Message);
                Debug.WriteLine("Task Inner Exception Message: "
                    + theTask.Exception.InnerException.Message);
            }
        }

        private async Task<string> DelayAsync()
        {
            await Task.Delay(100);

            // Uncomment each of the following lines to 
            // demonstrate exception handling. 

            //throw new OperationCanceledException("canceled");
            //throw new Exception("Something happened.");
            return "Done";
        }

        // Output when no exception is thrown in the awaited method: 
        //   Result: Done 
        //   Task IsCanceled: False 
        //   Task IsFaulted:  False 

        // Output when an Exception is thrown in the awaited method: 
        //   Exception Message: Something happened. 
        //   Task IsCanceled: False 
        //   Task IsFaulted:  True 
        //   Task Exception Message: One or more errors occurred. 
        //   Task Inner Exception Message: Something happened. 

        // Output when a OperationCanceledException or TaskCanceledException 
        // is thrown in the awaited method: 
        //   Exception Message: canceled 
        //   Task IsCanceled: True 
        //   Task IsFaulted:  False

El ejemplo siguiente se muestra el control de excepciones donde varias tareas pueden producir excepciones. El bloque try espera la tarea devuelta por una llamada a Task.WhenAll. Finaliza la tarea cuando se completan las tres tareas a las que se aplica WhenAll.

Cada una de las tres causas de tareas una excepción. El bloque catch recorre las excepciones, que se encuentran en la propiedad Exception.InnerExceptions de la tarea que se devuelta por Task.WhenAll.

public async Task DoMultipleAsync()
{
    Task theTask1 = ExcAsync(info: "First Task");
    Task theTask2 = ExcAsync(info: "Second Task");
    Task theTask3 = ExcAsync(info: "Third Task");

    Task allTasks = Task.WhenAll(theTask1, theTask2, theTask3);

    try
    {
        await allTasks;
    }
    catch (Exception ex)
    {
        Debug.WriteLine("Exception: " + ex.Message);
        Debug.WriteLine("Task IsFaulted: " + allTasks.IsFaulted);
        foreach (var inEx in allTasks.Exception.InnerExceptions)
        {
            Debug.WriteLine("Task Inner Exception: " + inEx.Message);
        }
    }
}

private async Task ExcAsync(string info)
{
    await Task.Delay(100);

    throw new Exception("Error-" + info);
}

// Output: 
//   Exception: Error-First Task 
//   Task IsFaulted: True 
//   Task Inner Exception: Error-First Task 
//   Task Inner Exception: Error-Second Task 
//   Task Inner Exception: Error-Third Task

Especificación del lenguaje C#

Para obtener más información, consulte la Especificación del lenguaje C#. La especificación del lenguaje es la fuente definitiva de la sintaxis y el uso de C#.

Vea también

Tareas

Cómo: Iniciar excepciones explícitamente

Referencia

Palabras clave de C#

Instrucciones try, throw y catch (C++)

Instrucciones para el control de excepciones (Referencia de C#)

throw (Referencia de C#)

try-finally (Referencia de C#)

Conceptos

Guía de programación de C#

Otros recursos

Referencia de C#