1. Introdução
Quando o programa fica um pouco mais complicado que um simples Hello, world!, rapidinho você se depara com a tarefa de tratar erros de tipos diferentes de jeitos diferentes. Imagina: você tá mexendo com arquivos, rede, banco de dados — e pra cada situação de erro a reação tem que ser diferente. Por exemplo, se o arquivo não existe, pode sugerir pro usuário escolher outro, mas se for erro de rede — tenta de novo ou mostra aquela mensagem animadora "Confere o cabo, vai que o gato mastigou de novo".
No C# a gente usa vários blocos catch em sequência pra isso. Cada bloco desses é especializado num tipo de exception (e seus filhos).
Estrutura de múltiplos catch
try
{
// Aqui vai o código que pode lançar vários tipos de exceptions
}
catch (FileNotFoundException ex)
{
Console.WriteLine($"Arquivo não encontrado: {ex.Message}");
}
catch (IOException ex) // pega todos os erros de I/O, exceto FileNotFoundException
{
Console.WriteLine($"Erro de entrada/saída: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"Algo deu errado: {ex.Message}");
}
Importante! O compilador vai passando pelos blocos de cima pra baixo e escolhe o primeiro que bate com o tipo. Se pegou um FileNotFoundException, ele nem olha os próximos.
Ilustração da "cadeia"
| Tipo de exception | Qual bloco pega? |
|---|---|
| FileNotFoundException | Primeiro catch |
| IOException (outro) | Segundo catch |
| ArgumentException | Terceiro (geral) catch |
Por que não misturar?
A armadilha mais "ampla" — tipo catch (Exception) — sempre deixa por último, senão ela engole todos os exceptions antes da hora e os catch mais específicos nunca vão ser executados.
É tipo desligar todos os alarmes de incêndio do prédio só porque um disparou por causa da torradeira: se rolar outro incêndio depois, o sistema nem percebe.
Exemplo prático
using System;
using System.IO;
class Program
{
static void Main()
{
try
{
double result = CalculateAverageAgeFromFile("users.txt");
Console.WriteLine($"Idade média — {result}");
}
catch (FileNotFoundException ex)
{
Console.WriteLine("Erro: arquivo não encontrado. Confere o caminho aí.");
}
catch (FormatException ex)
{
Console.WriteLine("Erro nos dados: não consegui ler a idade.");
}
catch (Exception ex)
{
Console.WriteLine($"Outro erro: {ex.Message}");
}
}
static double CalculateAverageAgeFromFile(string filePath)
{
// (Implementação: lê o arquivo, faz o parse das idades, calcula a média)
// ...
throw new NotImplementedException();
}
}
Aqui a gente separa direitinho o que fazer se não tem arquivo (FileNotFoundException), e o que fazer se os dados do arquivo tão "zoado" (FormatException). Todo o resto cai no terceiro bloco, o "reserva".
2. Filtros catch: pegando nuances
Vários blocos catch são massa, mas às vezes não basta. Tem hora que, dentro de um mesmo tipo de exception, você quer reagir diferente dependendo das circunstâncias.
Por exemplo, se a rede não funciona porque acabou a internet — dá pra tentar reconectar. Mas se o servidor não responde — talvez seja melhor mostrar outra mensagem.
É aí que entram os filtros catch — uma super feature do C# que deixa você capturar não só pelo tipo, mas também por uma condição extra.
Sintaxe do filtro when
catch (IOException ex) when (ex.Message.Contains("диска нет"))
{
Console.WriteLine("Opa! Parece que você tirou o pendrive.");
}
catch (IOException ex)
{
Console.WriteLine("Outro erro de entrada/saída: " + ex.Message);
}
Aqui o primeiro bloco pega só os IOException cujo message contém "диска нет" — o resto vai pro segundo bloco.
Usando na vida real
Filtros são especialmente úteis pra tratar erros de rede, quando você precisa decidir, baseado nas propriedades do exception, se tenta de novo ou só avisa do erro.
Outro exemplo: imagina que no método que faz o parse do arquivo, você quer tratar diferente quando o FormatException é por causa da idade (tipo, se a idade tá "abc" em vez de número).
catch (FormatException ex) when (ex.Message.Contains("idade"))
{
Console.WriteLine("Erro: não consegui ler a idade. Confere os dados aí.");
}
catch (FormatException ex)
{
Console.WriteLine("Erro de formato dos dados: " + ex.Message);
}
Filtros e performance
Filtros são práticos, mas lembra que a condição do filtro é avaliada antes de entrar no bloco catch, ou seja, se não passar — o corpo do bloco nem roda.
Aliás, se o filtro lançar um exception (tipo, se no when rolar uma divisão por zero) — esse exception catch nunca vai pegar. Tem que tomar cuidado com isso.
3. Misturando múltiplos catch e filtros
Imagina que no seu projeto você precisa tratar não só o tipo do exception, mas também o estado interno dele. Por exemplo, ao lidar com IOException você quer um comportamento pra erro de permissão e outro pra falta de espaço no disco.
try
{
File.AppendAllText("log.txt", "Novo registro\n");
}
catch (IOException ex) when (ex.Message.Contains("Sem espaço"))
{
Console.WriteLine("Erro: Acabou o espaço no disco!");
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("Erro: sem permissão pra gravar o arquivo. Tenta rodar como admin.");
}
catch (IOException ex)
{
Console.WriteLine("Outro erro de disco: " + ex.Message);
}
Aqui a gente usa filtro e separação por tipo de erro. Essa flexibilidade é muito útil quando você tá desenvolvendo um app mais parrudo, onde é importante dar respostas informativas pra falhas comuns.
4. Particularidades e "pegadinhas" dos filtros e múltiplos catch
- O filtro catch pode usar a variável do exception (ex), mas não pode alterar ela.
- Se você lançar um exception dentro do when, ele não vai ser "pego" por esse catch — ele sobe na stack como se o filtro nem existisse.
- A lógica do filtro vira parte do contrato: seu colega de projeto tem que saber que nem todo IOException vai ser capturado — só se a condição bater.
- Se você usar só um catch no método todo, mas com filtro, pode acabar não pegando alguns exceptions. Se tiver dúvida, usa blocos separados mesmo.
- Em apps grandes, dá pra usar filtros pra centralizar lógica, tipo logar só erros críticos.
5. Cenários pra entrevistas e vida real
Saber usar filtros e múltiplos catch não é só pra deixar o código bonito ou ganhar ponto de clean code. É uma skill real que podem te pedir em entrevistas, principalmente se a vaga for pra manter um sistema grande, complexo, distribuído.
Exemplos de perguntas:
- Como em C# tratar diferentes cenários de erro ao ler um arquivo, pra mostrar mensagens diferentes pra cada situação (arquivo não existe, disco ocupado, dados corrompidos)?
- Como não "abafar" um erro importante se colocou um bloco geral catch (Exception)?
- Pra que serve o filtro catch? Pode ter um throw dentro dele?
GO TO FULL VERSION