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 seguintes 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 matrizargs
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
, ,continue
break
, ougoto
), 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.
A execução do finally
bloco depende se o sistema operacional opta por acionar uma operação de desenrolar exceção. 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 OverflowException InvalidProgramException 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#: