Declarações de tratamento de exceções - throw, try-catch, try-finally, e try-catch-finally

Você usa as throw instruções e try para trabalhar com exceções. Use a throw instrução para lançar uma exceção. Use a instrução para capturar e manipular exceções que podem ocorrer durante a try execução de um bloco de código.

A throw declaração

A throw declaração lança uma exceção:

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

Em uma throw e; declaração, o resultado da expressão e deve ser implicitamente conversível em System.Exception.

Você pode usar as classes de exceção internas, por exemplo, ArgumentOutOfRangeException ou InvalidOperationException. O .NET também fornece os métodos auxiliares para lançar exceções em determinadas condições: ArgumentNullException.ThrowIfNull e ArgumentException.ThrowIfNullOrEmpty. Você também pode definir suas próprias classes de exceção que derivam de System.Exception. Para obter mais informações, consulte Criando e lançando exceções.

Dentro de um catch bloco, você pode usar uma throw; instrução para relançar a exceção que é manipulada catch pelo bloco:

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

Nota

throw; Preserva o rastreamento de pilha original da exceção, que é armazenado na Exception.StackTrace propriedade. Oposto a isso, throw e; atualiza a StackTrace propriedade de e.

Quando uma exceção é lançada, o Common Language Runtime (CLR) procura o catch bloco que pode lidar com essa exceção. Se o método atualmente executado não contiver esse catch bloco, o CLR examinará o método que chamou o método atual e assim por diante na pilha de chamadas. Se nenhum catch bloco for encontrado, o CLR encerrará o thread em execução. Para obter mais informações, consulte a seção Como as exceções são tratadas da especificação da linguagem C#.

A throw expressão

Você também pode usar throw como uma expressão. Isso pode ser conveniente em vários casos, que incluem:

  • o operador condicional. O exemplo a seguir usa uma throw expressão para lançar um ArgumentException quando a matriz args passada está vazia:

    string first = args.Length >= 1 
        ? args[0]
        : throw new ArgumentException("Please supply at least one argument.");
    
  • o operador de coalescência nulo. O exemplo a seguir usa uma throw expressão para lançar um ArgumentNullException quando a cadeia de caracteres a ser atribuída a uma propriedade é null:

    public string Name
    {
        get => name;
        set => name = value ??
            throw new ArgumentNullException(paramName: nameof(value), message: "Name cannot be null");
    }
    
  • um lambda ou método com corpo de expressão. O exemplo a seguir usa uma throw expressão para lançar um InvalidCastException para indicar que uma conversão em um DateTime valor não é suportada:

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

A try declaração

Você pode usar a try instrução em qualquer uma das seguintes formas: try-catch - para lidar com exceções que podem ocorrer durante a execução do código dentro de um try bloco, try-finally - para especificar o código que é executado quando o controle deixa o try bloco, e try-catch-finally - como uma combinação dos dois formulários anteriores.

A try-catch declaração

Use a instrução para manipular exceções que podem ocorrer durante a try-catch execução de um bloco de código. Coloque o código onde uma exceção pode ocorrer dentro de um try bloco. Use uma cláusula catch para especificar o tipo base de exceções que você deseja manipular no bloco correspondente catch :

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

Pode fornecer várias cláusulas de captura:

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

Quando ocorre uma exceção, as cláusulas de captura são examinadas pela ordem especificada, de cima para baixo. No máximo, apenas um catch bloco é executado para qualquer exceção lançada. Como o exemplo anterior também mostra, você pode omitir a declaração de uma variável de exceção e especificar apenas o tipo de exceção em uma cláusula catch. Uma cláusula de captura sem qualquer tipo de exceção especificado corresponde a qualquer exceção e, se existir, deve ser a última cláusula de captura.

Se você quiser lançar novamente uma exceção capturada, use a throw instrução, como mostra o exemplo a seguir:

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

Nota

throw; Preserva o rastreamento de pilha original da exceção, que é armazenado na Exception.StackTrace propriedade. Oposto a isso, throw e; atualiza a StackTrace propriedade de e.

Um filtro de when exceção

Junto com um tipo de exceção, você também pode especificar um filtro de exceção que examina ainda mais uma exceção e decide se o bloco correspondente catch manipula essa exceção. Um filtro de exceção é uma expressão booleana que segue a when palavra-chave, como mostra o exemplo a seguir:

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

O exemplo anterior usa um filtro de exceção para fornecer um único catch bloco para manipular exceções de dois tipos especificados.

Você pode fornecer várias catch cláusulas para o mesmo tipo de exceção se elas se distinguirem por filtros de exceção. Uma dessas cláusulas pode não ter filtro de exceção. Se essa cláusula existir, deve ser a última das cláusulas que especificam esse tipo de exceção.

Se uma catch cláusula tiver um filtro de exceção, ela poderá especificar o tipo de exceção que é igual ou menos derivado do que um tipo de exceção de uma catch cláusula que aparece depois dela. Por exemplo, se um filtro de exceção estiver presente, uma catch (Exception e) cláusula não precisa ser a última cláusula.

Exceções em métodos assíncronos e iteradores

Se ocorrer uma exceção em uma função assíncrona, ela se propagará para o chamador da função quando você aguardar o resultado da função, como mostra o exemplo a seguir:

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

Se ocorrer uma exceção em um método iterador, ela se propagará para o chamador somente quando o iterador avançar para o próximo elemento.

A try-finally declaração

Em uma try-finally instrução, o bloco é executado quando o finally controle sai do try bloco. O controle pode deixar o try bloco como resultado de

  • execução normal,
  • execução de uma instrução jump (ou seja, return, , continuebreak, ou goto), ou
  • propagação de uma exceção fora do try bloco.

O exemplo a seguir usa o finally bloco para redefinir o estado de um objeto antes que o controle deixe o método:

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

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

Você também pode usar o finally bloco para limpar os recursos alocados try usados no bloco.

Nota

Quando o tipo de um recurso implementa a IDisposable interface ou IAsyncDisposable , considere a using instrução. A using declaração garante que os recursos adquiridos sejam descartados quando o controle deixar a using declaração. O compilador transforma uma using instrução em uma try-finally instrução.

Em quase todos os casos finally , os blocos são executados. Os únicos casos em que finally os blocos não são executados envolvem o encerramento imediato de um programa. Por exemplo, tal rescisão pode acontecer por causa da Environment.FailFast chamada ou de uma OverflowExceptionInvalidProgramException ou exceção. A maioria dos sistemas operacionais executa uma limpeza razoável de recursos como parte da parada e descarregamento do processo.

A try-catch-finally declaração

Você usa uma try-catch-finally instrução para manipular exceções que podem ocorrer durante a execução do bloco e especificar o código que deve ser executado quando o try controle deixa a try instrução:

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

}

Quando uma exceção é tratada por um catch bloco, o bloco é executado após a finally execução desse bloco (mesmo que catch outra exceção ocorra durante a execução do catch bloco). Para obter informações sobre catch e finally blocos, consulte As seções A try-catch instrução e A try-finally instrução , respectivamente.

Especificação da linguagem C#

Para obter mais informações, consulte as seguintes seções da especificação da linguagem C#:

Consulte também