try-catch (Справочник по C#)

Оператор try-catch состоит из блока try, за которым следует одно или несколько предложений catch, в которых определяются обработчики для различных исключений.

Заметки

При возникновении исключения среда CLR ищет оператор catch, который обрабатывает это исключение. Если выполняющийся в данный момент метод не содержит такого блока catch, то среда CLR рассматривает метод, который вызвал текущий метод, и т. д. по стеку вызовов. Если блок catch не найден, то среда CLR отображает пользователю сообщение о необработанном исключении и останавливает выполнение программы.

Блок try содержит защищаемый код, в котором могут происходить исключения. Этот блок выполняется до момента возникновения исключения или до своего успешного завершения. Например, следующая попытка приведения объекта null вызовет исключение NullReferenceException.

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

Хотя предложение catch можно использовать без аргументов для перехвата любого типа исключения, такой подход не рекомендуется. В общем случае следует перехватывать только те исключения, устранение причин которых известно. Поэтому следует всегда задавать аргумент объекта, производного от Exception. Например:

catch (InvalidCastException e) 
{
}

В одном и том же операторе try-catch можно использовать несколько предложений catch. В этом случае будет иметь значение порядок следования предложений catch, поскольку предложения catch будут проверяться именно в этом порядке. Более общие исключения следует перехватывать после более частных. При упорядочивании блоков catch таким образом, чтобы последующий блок был недостижимым, компилятор вызовет ошибку.

В блок catch можно включить оператор throw, чтобы заново создать исключение, перехваченное оператором catch. В следующем примере извлекается источник информации из исключение IOException и затем выдается исключение для родительского метода.

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;
}

Можно перехватить одно исключение и создать другое исключение. При этом необходимо задать исключение, перехваченное в качестве внутреннего, как показано в следующем примере.

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

Кроме того, можно повторно создать исключение, если заданное условие выполнено, как показано в следующем примере.

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

Из блока try следует инициализировать только те переменные, которые в нем объявлены. В противном случае исключение может произойти до того, как завершится выполнение блока. Например, в следующем примере кода переменная n инициализируется внутри блока try. Попытка использовать эту переменную вне блока try в операторе Write(n) приведет к ошибке компиляции.

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);
}

Дополнительные сведения о перехвате исключений см. в разделе try-catch-finally.

Исключения в Асинхронных методов

Метод асинхронного помечен модификатором async и обычно содержит один или несколько ожидает выражений или выписок. Выражение ожидания применяется оператор подождите к Task или Task. Выражение await не может произойти в блоке catch или блоке finally.

Если элемент управления достигает await в методе async, хода выполнения в методе приостанавливается до тех пор, пока подожданная задача не завершится. Если задача завершена, выполнение может возобновить в методе. Дополнительные сведения см. в разделах Асинхронное программирование с использованием ключевых слов Async и Await (C# и Visual Basic) и Поток управления в асинхронных программах (C# и Visual Basic).

Завершенная задача, к которой await применено может быть в состоянии сбоя из-за необработанного исключения в методе, который возвращает задачу. Ожидание задачи вызывает исключение. Задача также может выполнить поиск в отмененном состоянии, если асинхронный процесс, который возвращениям он отменяет. Ожидание отмененной задачи вызывает OperationCanceledException. Дополнительные сведения о том, как отменить процесс асинхронной см. в разделе Настройка асинхронного приложения (C# и Visual Basic).

Перехват исключения, подождать задачу в блоке try и перехватывать исключения в соответствующем блоке catch. Пример см. в подразделе "пример".

Задача может находиться в состоянии сбоя, так как несколько исключений в методе подожданном async. Например, задача может быть результатом вызова метода Task.WhenAll. Если ожидается такой задачи, перехватывается только одно из исключений, и невозможно предсказать, перехватывается исключение будет. Пример см. в подразделе "пример".

Пример

В следующем примере блок try содержит обращение к методу ProcessString, который может вызвать исключение. В предложении catch содержится обработчик исключения, который просто выводит на экран сообщение. Если оператор throw вызывается из метода MyMethod, система осуществляет поиск оператора catch и выводит на экран сообщение 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.
     * */

В следующем примере используются 2 блока catch и перехватывается самое конкретное исключение, который сначала.

Для перехвата наименьшее конкретное исключение, можно заменить оператор с ProcessString в следующей выпиской: throw new Exception().

Если блок catch наименьш- определенной в примере, то появится следующее сообщение об ошибке: 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.
*/

Следующий пример иллюстрирует обработку ошибок для асинхронных методов. Перехват исключения, штрихи задачи async, задать выражение await в блоке try и перехватывает исключения в блоке catch.

Раскомментируйте линия throw new Exception в примере для демонстрации обработку ошибок. Свойство IsFaulted задачи устанавливается в True свойство установлено Exception.InnerException задачи и исключения и исключение перехватывается в блоке catch.

Раскомментируйте линия throw new OperationCancelledException для демонстрации того, что произойдет при нажатии cancelan асинхронный процесс. Свойство IsCanceled задачи устанавливается в true и исключение перехватывается в блоке catch. При определенных условиях, которые не применяются к данному примеру, свойство IsFaulted задачи устанавливается в true и IsCanceled установлено в 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

Следующий пример иллюстрирует обработку исключений, где несколько задач могут привести к появлению нескольких исключении. Блок try ожидает задачи, возвращаемый вызовом Task.WhenAll. Задача завершена, когда 3 задачи, к которой WhenAll применено завершения.

Каждая из задач 3 причин исключения. Блок catch перебирает исключения, которые найдены в свойстве Exception.InnerExceptions задачи, возвращенной 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

Спецификация языка C#

Дополнительные сведения см. в Спецификация языка C#. Спецификация языка является предписывающим источником информации о синтаксисе и использовании языка C#.

См. также

Задачи

Практическое руководство. Явное создание исключения

Ссылки

Ключевые слова C#

Операторы try, throw и catch (C++)

Операторы обработки исключений (Справочник по C#)

throw (Справочник по C#)

try-finally (Справочник по C#)

Основные понятия

Руководство по программированию на C#

Другие ресурсы

Справочник по C#