Операторы обработки исключений — throw, , try-finallytry-catchиtry-catch-finally

Для работы с исключениями используются throw инструкции и try инструкции. Используйте инструкцию throw для создания исключения. Используйте инструкцию try для перехвата и обработки исключений, которые могут возникать во время выполнения блока кода.

Инструкция throw

Оператор throw создает исключение:

if (shapeAmount <= 0)
{
    throw new ArgumentOutOfRangeException(nameof(shapeAmount), "Amount of shapes must be positive.");
}

В операторе throw e; результат выражения e должен быть неявно преобразован в System.Exception.

Можно использовать встроенные классы исключений, например ArgumentOutOfRangeException или InvalidOperationException. .NET также предоставляет вспомогательные методы для создания исключений в определенных условиях: ArgumentNullException.ThrowIfNull и ArgumentException.ThrowIfNullOrEmpty. Вы также можете определить собственные классы исключений, производные от System.Exception. Дополнительные сведения см. в разделе "Создание и создание исключений".

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

try
{
    ProcessShapes(shapeAmount);
}
catch (Exception e)
{
    LogError(e, "Shape processing failed.");
    throw;
}

Примечание.

throw; сохраняет исходную трассировку стека исключения, которая хранится в свойстве Exception.StackTrace . Напротив этого throw e; обновляется StackTrace свойство e.

При возникновении исключения среда CLR ищет catch блок , который может обрабатывать это исключение. Если текущий выполняемый метод не содержит такой catch блок, среда CLR смотрит на метод, который вызывает текущий метод, и т. д. в стеке вызовов. Если блок не catch найден, среда CLR завершает исполняемый поток. Дополнительные сведения см. в разделе "Обработка исключений" спецификации языка C#.

Выражение throw

Вы также можете использовать throw в качестве выражения. Это может быть удобно в ряде случаев, в том числе:

  • Условный оператор. В следующем примере используется выражение для созданияArgumentException, когда переданный throw массив args пуст:

    string first = args.Length >= 1 
        ? args[0]
        : throw new ArgumentException("Please supply at least one argument.");
    
  • Оператор объединения с NULL. В следующем примере используется throw выражение для создания ArgumentNullException строки для назначения свойству null:

    public string Name
    {
        get => name;
        set => name = value ??
            throw new ArgumentNullException(paramName: nameof(value), message: "Name cannot be null");
    }
    
  • Метод или лямбда, воплощающие выражение. В следующем примере используется throw выражение для создания выражения InvalidCastException , указывающего, что преобразование в DateTime значение не поддерживается:

    DateTime ToDateTime(IFormatProvider provider) =>
             throw new InvalidCastException("Conversion to a DateTime is not supported.");
    

Инструкция try

Инструкцию try можно использовать в любой из следующих форм: try-catch для обработки исключений, которые могут возникать во время выполнения кода внутри try блока, try-finally — для указания кода, выполняемого при выходе try элемента управления из блока, и try-catch-finally в сочетании с предыдущими двумя формами.

Инструкция try-catch

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

try
{
    var result = Process(-3, 4);
    Console.WriteLine($"Processing succeeded: {result}");
}
catch (ArgumentException e)
{
    Console.WriteLine($"Processing failed: {e.Message}");
}

Можно указать несколько предложений catch:

try
{
    var result = await ProcessAsync(-3, 4, cancellationToken);
    Console.WriteLine($"Processing succeeded: {result}");
}
catch (ArgumentException e)
{
    Console.WriteLine($"Processing failed: {e.Message}");
}
catch (OperationCanceledException)
{
    Console.WriteLine("Processing is cancelled.");
}

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

Если вы хотите повторно создать исключение, используйте инструкциюthrow, как показано в следующем примере:

try
{
    var result = Process(-3, 4);
    Console.WriteLine($"Processing succeeded: {result}");
}
catch (Exception e)
{
    LogError(e, "Processing failed.");
    throw;
}

Примечание.

throw; сохраняет исходную трассировку стека исключения, которая хранится в свойстве Exception.StackTrace . Напротив этого throw e; обновляется StackTrace свойство e.

Фильтр исключений when

Наряду с типом исключения можно также указать фильтр исключений, который дополнительно проверяет исключение и решает, обрабатывает ли соответствующее catch исключение блок. Фильтр исключений — это логическое выражение, when следующее за ключевое слово, как показано в следующем примере:

try
{
    var result = Process(-3, 4);
    Console.WriteLine($"Processing succeeded: {result}");
}
catch (Exception e) when (e is ArgumentException || e is DivideByZeroException)
{
    Console.WriteLine($"Processing failed: {e.Message}");
}

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

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

catch Если предложение имеет фильтр исключений, он может указать тип исключения, который совпадает с или менее производным, чем тип catch исключения предложения, который отображается после него. Например, если фильтр исключений присутствует, catch (Exception e) предложение не должно быть последним предложением.

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

Если исключение возникает в асинхронной функции, он распространяется на вызывающий объект функции при ожидании результата функции, как показано в следующем примере:

public static async Task Run()
{
    try
    {
        Task<int> processing = ProcessAsync(-1);
        Console.WriteLine("Launched processing.");

        int result = await processing;
        Console.WriteLine($"Result: {result}.");
    }
    catch (ArgumentException e)
    {
        Console.WriteLine($"Processing failed: {e.Message}");
    }
    // Output:
    // Launched processing.
    // Processing failed: Input must be non-negative. (Parameter 'input')
}

private static async Task<int> ProcessAsync(int input)
{
    if (input < 0)
    {
        throw new ArgumentOutOfRangeException(nameof(input), "Input must be non-negative.");
    }

    await Task.Delay(500);
    return input;
}

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

Инструкция try-finally

В инструкции try-finallyfinally блок выполняется при выходе try элемента управления из блока. Элемент управления может оставить try блок в результате

  • нормальное выполнение,
  • выполнение инструкции перехода (т. е. , breakreturn, continueили ) или goto
  • распространение исключения из try блока.

В следующем примере блок используется finally для сброса состояния объекта перед выходом элемента управления из метода:

public async Task HandleRequest(int itemId, CancellationToken ct)
{
    Busy = true;

    try
    {
        await ProcessAsync(itemId, ct);
    }
    finally
    {
        Busy = false;
    }
}

Вы также можете использовать finally блок для очистки выделенных ресурсов, используемых в блоке try .

Примечание.

Если тип ресурса реализует IDisposable или интерфейс, рассмотрите инструкциюusing.IAsyncDisposable Инструкция using гарантирует, что приобретенные ресурсы удаляются при выходе элемента using управления из оператора. Компилятор преобразует using оператор в try-finally оператор.

В почти всех случаях finally выполняются блоки. Единственные случаи, когда finally блоки не выполняются, включают немедленное завершение программы. Например, такое завершение может произойти из-за Environment.FailFast вызова или OverflowExceptionInvalidProgramException исключения. Большинство операционных систем выполняют разумное очистку ресурсов в рамках остановки и выгрузки процесса.

Инструкция try-catch-finally

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

public async Task ProcessRequest(int itemId, CancellationToken ct)
{
    Busy = true;

    try
    {
        await ProcessAsync(itemId, ct);
    }
    catch (Exception e) when (e is not OperationCanceledException)
    {
        LogError(e, $"Failed to process request for item ID {itemId}.");
        throw;
    }
    finally
    {
        Busy = false;
    }

}

Если исключение обрабатывается блоком catch , finally блок выполняется после выполнения этого catch блока (даже если во время выполнения catch блока возникает другое исключение). Сведения о инструкциях и finally блоках см. в разделах try-catch инструкции и инструкции try-finally соответственно.catch

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

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

См. также