Exceptions & Error Handling
Try/catch/finally, hierarquia de exceptions, guard clauses, async exceptions e boas praticas em C#
Regra de ouro: Use exceptions para sinalizar condicoes inesperadas e excepcionais β nunca para controle de fluxo normal. Prefira exceptions especificas (
ArgumentNullException, InvalidOperationException) e valide cedo com guard clauses. Sempre faca cleanup em finally / using / await using. Hierarquia de Exceptions no .NET
Try / Catch / Finally β mecanica basica
O bloco try delimita codigo que pode falhar. catch captura exceptions especificas. finally executa sempre β sucesso ou falha β ideal para cleanup.
// Try/Catch/Finally β mecanica basica
try
{
DoWork();
}
catch (IOException ex)
{
Console.Error.WriteLine($"I/O failed: {ex.Message}");
// maybe retry, maybe surface to caller
}
finally
{
Console.WriteLine("Always runs (cleanup/logging).");
}
Ordem dos catches: especifico antes de generico
Ordene catches do mais especifico para o mais generico. Exception filters com when permitem branching por propriedades da exception sem if aninhados. O filtro roda antes do handler executar.
// Ordene catches do mais especifico para o mais generico
// Filters com 'when' permitem branching sem if aninhado
try
{
SaveFile(path);
}
catch (IOException ex) when (IsTransient(ex))
{
Console.Error.WriteLine("Transient I/Oβretrying once...");
SaveFile(path); // simplistic single retry
}
catch (IOException ex)
{
Console.Error.WriteLine($"Non-transient I/O: {ex.Message}");
throw; // rethrow preserving stack
}
throw vs throw ex β preservar stack trace
Use throw; para re-lancar preservando o stack trace original. throw ex; reseta o stack trace para o ponto atual, dificultando diagnostico.
// throw; preserva stack trace original
// throw ex; reseta stack trace β EVITE
try
{
Dangerous();
}
catch (Exception ex)
{
Log(ex);
throw; // preserves original stack trace
// throw ex; // resets stack trace to this point β BAD
}
Quando lancar vs quando capturar
| Situacao | Acao | Por que |
|---|---|---|
| Argumento invalido | throw imediatamente | Fail fast β nao deixe estado corrompido propagar |
| Erro transiente (I/O, rede) | catch + retry | Pode se recuperar com nova tentativa |
| Erro numa boundary (API, service) | catch + wrap | Traduz para exception de dominio com contexto |
| Erro que voce nao sabe tratar | Deixe propagar | Alguem acima sabe; nao engula silenciosamente |
| Cancelamento (OperationCanceledException) | Propague ou trate distinctamente | Nao e erro β e cooperacao. Nao wrape como falha |
throw vs throw ex?
throw; preserva o stack trace original intacto β voce ve exatamente onde a exception originou. throw ex; reseta o stack trace para o ponto do rethrow, destruindo a informacao da origem. Sempre use throw; a menos que intencionalmente queira esconder a origem (raro). Em .NET 6+, ExceptionDispatchInfo.Throw(ex) e outra opcao que preserva tudo.Quando capturar vs deixar exceptions propagarem?
Capture apenas se voce pode fazer algo util: retry, wrap com contexto, traduzir para resposta HTTP, ou logar e re-lancar. Se nao sabe o que fazer, deixe propagar β um handler mais acima (middleware, global handler) vai tratar. Engolir exceptions (
catch { } vazio) e um dos piores antipatterns: esconde bugs e corrompe estado silenciosamente.