C# try e catch
Na aula Exceções, você viu o programa "quebrar": uma exceção não tratada encerra tudo, com o código de saída 134. Agora você vai aprender a tratar a exceção: dizer ao C# "tente fazer isto; se der este problema, faça aquilo" e deixar o programa seguir em frente. As palavras para isso são try e catch, e o C# ainda tem um recurso a mais, os filtros when.
- Escrever um bloco
try/catche entender o caminho que o programa percorre. - Capturar uma exceção específica, e o que acontece com as outras.
- Pegar uma família inteira com a classe mãe, e por que
catch (Exception)é perigoso. - Usar vários
catch, e por que a ordem entre eles importa (CS0160). - Escolher com uma condição qual
catchtrata o problema: os filtroswhen. - Tirar informação da exceção:
Message,GetType()eToString(). - Capturar uma exceção que nasceu dentro de outro método.
- Por que um
catchvazio ("engolir a exceção") é um dos piores hábitos.
Todos os programas rodaram na máquina da Ana, com o .NET 10 (C# 14), usando dotnet run arquivo.cs na pasta ~/cs.
A ideia antes do código
Pense num trapezista de circo. Ele tenta o salto. Quase sempre dá certo. Mas, se ele cair, há uma rede de proteção embaixo que o pega, e o espetáculo continua. Sem a rede, uma queda encerra o show.
No C#:
try("tente") é o salto: o código que pode dar problema.catch("pegue", "capture") é a rede: o que fazer se uma exceção de certo tipo for lançada dentro dotry.
O primeiro try/catch
A Ana recebe idades como texto (de um formulário, de um arquivo) e precisa transformá-las em número com int.Parse. Se o texto não for um número, o int.Parse lança FormatException. Desta vez, o programa vai estar preparado:
string texto = "29 anos";
try
{
int idade = int.Parse(texto);
Console.WriteLine($"Idade: {idade}");
}
catch (FormatException)
{
Console.WriteLine($"Isso não é um número: {texto}");
}
Console.WriteLine("Fim do programa");
- try
- Abre o bloco protegido. Tudo entre as chaves logo abaixo roda normalmente, mas "vigiado".
- int idade = int.Parse(texto);
- A linha que pode lançar a exceção.
- Console.WriteLine($"Idade: {idade}");
- Só roda se a linha de cima deu certo.
- catch (FormatException)
- A "rede". Os parênteses dizem que tipo de exceção esta rede pega: aqui, só
FormatException. - { … }
- O que fazer quando a exceção acontece: aqui, avisar com uma mensagem amigável.
- Console.WriteLine("Fim do programa");
- Fora do
try/catch: roda nos dois casos.
$ dotnet run idade.cs Isso não é um número: 29 anos Fim do programa $ echo $? 0
Compare com a aula anterior: nada de Unhandled exception, o Fim do programa apareceu e o código de saída é 0 ("terminou bem"). A exceção aconteceu do mesmo jeito, mas foi tratada.
O caminho do programa
Repare que Idade: … não apareceu. Quando uma linha dentro do try lança uma exceção, o C# abandona o resto do try e pula direto para o catch. Depois do catch, segue com o que vem depois do bloco todo. Os dois caminhos possíveis:
| Linha | Com "29 anos" (exceção) | Com "29" (tudo certo) |
|---|---|---|
4 int.Parse(texto) | lança FormatException | roda: idade = 29 |
5 WriteLine("Idade: …") | pulada | roda |
9 WriteLine("Isso não é…") | roda | pulada (sem exceção, o catch não roda) |
11 WriteLine("Fim…") | roda | roda |
Trocando "29 anos" por "29", a saída real é:
$ dotnet run idade.cs Idade: 29 Fim do programa
Missão: rode o programa e veja-o quebrar. Depois proteja as duas linhas do meio com try e um catch (FormatException) que mostre Isso não é um número. O Fim do programa deve aparecer.
string texto = "29 anos";
int idade = int.Parse(texto);
Console.WriteLine($"Idade: {idade}");
Console.WriteLine("Fim do programa");
string texto = "29 anos";
try
{
int idade = int.Parse(texto);
Console.WriteLine($"Idade: {idade}");
}
catch (FormatException)
{
Console.WriteLine($"Isso não é um número: {texto}");
}
Console.WriteLine("Fim do programa");
TryParse
Esta aula usa int.Parse porque é o jeito mais simples de provocar uma exceção. Num programa de verdade, quando o texto vem do teclado, int.TryParse é melhor: ele não lança exceção, só devolve false (aula Parse e TryParse). O fim desta aula compara as duas formas. O try/catch brilha mesmo com o que você não consegue prever: arquivos, rede, outros programas.
O catch só pega o tipo que você pediu
A rede do catch tem formato: catch (FormatException) pega só FormatException (e as classes filhas dela). Se outra exceção acontecer no try, ela passa direto pela rede:
int soma = 22;
int provas = 0;
try
{
Console.WriteLine($"Média: {soma / provas}");
}
catch (FormatException)
{
Console.WriteLine("Número inválido");
}
Console.WriteLine("Fim do programa");
$ dotnet run media.cs Unhandled exception. System.DivideByZeroException: Attempted to divide by zero. at Program.<Main>$(String[] args) in /home/ana/cs/media.cs:line 5
A divisão lançou DivideByZeroException, que não é uma FormatException. O catch foi ignorado, a exceção continuou "caindo" e encerrou o programa, como se o try nem existisse.
Pegar a classe mãe pega as filhas
Lembra da família? DivideByZeroException e OverflowException são filhas de ArithmeticException. Um catch da classe mãe pega qualquer filha. E, para saber qual foi o problema, dê um nome à exceção capturada:
int soma = 22;
int provas = 0;
try
{
Console.WriteLine($"Média: {soma / provas}");
}
catch (ArithmeticException e)
{
Console.WriteLine($"Problema: {e.Message}");
}
Console.WriteLine("Fim do programa");
$ dotnet run media.cs Problema: Attempted to divide by zero. Fim do programa
- catch (ArithmeticException e)
- Pega
ArithmeticExceptione todas as filhas. Oeé uma variável que recebe o objeto da exceção. Por costume ela se chamae(de exception) ouex, mas pode ter qualquer nome. - e.Message
- A mensagem da exceção, a mesma que aparece depois dos dois-pontos no relatório.
Sem precisar do objeto, escreva só o tipo, como no primeiro exemplo: catch (FormatException). Se você der um nome e não usar, o compilador avisa (CS0168, veja Quando dá errado).
Subindo mais, catch (Exception e) pega qualquer exceção, e catch sozinho, sem parênteses, também:
try
{
int[] v = [1];
Console.WriteLine(v[3]);
}
catch
{
Console.WriteLine("Deu algum problema.");
}
$ dotnet run semtipo.cs Deu algum problema.
Parece prático, mas é perigoso: você passa a tratar do mesmo jeito problemas muito diferentes, inclusive os que você nem imaginava (um NullReferenceException causado por um defeito no seu código, por exemplo). E a mensagem "Deu algum problema" não ajuda ninguém a consertar. A regra dos programadores experientes: capture a exceção mais específica que você sabe tratar. catch (Exception e) fica para um lugar só, no ponto mais alto do programa, para registrar o que escapou de tudo.
Vários catch
Um try pode ter vários catch, um para cada tipo de problema, cada um com a sua resposta. O C# testa os catch de cima para baixo e usa o primeiro que combinar:
string[] textos = ["10", "abc", "0", "99999999999"];
foreach (string t in textos)
{
try
{
int n = int.Parse(t);
Console.WriteLine($"100 / {n} = {100 / n}");
}
catch (FormatException)
{
Console.WriteLine($"\"{t}\" não é número");
}
catch (DivideByZeroException)
{
Console.WriteLine("Não dá para dividir por zero");
}
catch (OverflowException)
{
Console.WriteLine($"{t} é grande demais para um int");
}
}
$ dotnet run divisoes.cs 100 / 10 = 10 "abc" não é número Não dá para dividir por zero 99999999999 é grande demais para um int
O foreach passa pelos quatro textos. Cada volta tem o seu próprio try: "10" dá certo; "abc" cai no primeiro catch; "0" passa pelo int.Parse, mas a divisão cai no segundo; "99999999999" não cabe num int e cai no terceiro. E, como o try está dentro do laço, um problema numa volta não impede as outras.
A ordem importa
Como o C# usa o primeiro catch que combina, um catch da classe mãe antes de uma filha deixaria a filha inalcançável. O compilador percebe e recusa o programa:
try
{
int n = int.Parse("abc");
Console.WriteLine(100 / n);
}
catch (Exception e)
{
Console.WriteLine("Algo deu errado");
}
catch (FormatException e)
{
Console.WriteLine("Não é número");
}
$ dotnet run ordem.cs
/home/ana/cs/ordem.cs(10,8): error CS0160: Cláusula catch anterior já captura todas as exceções desta ou de um super tipo ("Exception")
/home/ana/cs/ordem.cs(6,18): warning CS0168: A variável "e" está declarada, mas nunca é usada
/home/ana/cs/ordem.cs(10,24): warning CS0168: A variável "e" está declarada, mas nunca é usada
Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
"Uma cláusula catch anterior já captura todas as exceções deste tipo ou de um supertipo (Exception)." Em inglês: A previous catch clause already catches all exceptions of this or of a super type. O catch (Exception e) da linha 6 já pega qualquer FormatException, então o da linha 10 nunca seria usado. Os dois avisos CS0168 dizem que os dois e não são usados. Regra: do mais específico (filha) para o mais geral (mãe). Inverter os dois catch resolve.
Missão: o programa trata texto inválido, mas quebra no "0". Acrescente um segundo catch, para DivideByZeroException, que mostre Não dá para dividir por zero, e faça o Fim aparecer.
string[] textos = ["10", "abc", "0"];
foreach (string t in textos)
{
try
{
int n = int.Parse(t);
Console.WriteLine($"100 / {n} = {100 / n}");
}
catch (FormatException)
{
Console.WriteLine($"\"{t}\" não é número");
}
}
Console.WriteLine("Fim");
string[] textos = ["10", "abc", "0"];
foreach (string t in textos)
{
try
{
int n = int.Parse(t);
Console.WriteLine($"100 / {n} = {100 / n}");
}
catch (FormatException)
{
Console.WriteLine($"\"{t}\" não é número");
}
catch (DivideByZeroException)
{
Console.WriteLine("Não dá para dividir por zero");
}
}
Console.WriteLine("Fim");
Filtros: catch … when (condição) C# 6
Às vezes o tipo da exceção não basta para decidir o que fazer. Um texto vazio e um texto com letras dão a mesma FormatException, mas a Ana quer responder diferente a cada um. O filtro when ("quando") acrescenta uma condição ao catch: ele só pega a exceção se o tipo combinar e a condição for true:
string[] entradas = ["12", "", "doze", "99999999999"];
foreach (string texto in entradas)
{
try
{
int quantidade = int.Parse(texto);
Console.WriteLine($"Quantidade: {quantidade}");
}
catch (FormatException) when (texto == "")
{
Console.WriteLine("Você não digitou nada.");
}
catch (FormatException)
{
Console.WriteLine($"\"{texto}\" não é um número.");
}
catch (Exception e) when (e is OverflowException or ArgumentNullException)
{
Console.WriteLine($"Valor fora do esperado: {e.GetType().Name}");
}
}
$ dotnet run filtro.cs Quantidade: 12 Você não digitou nada. "doze" não é um número. Valor fora do esperado: OverflowException
- catch (FormatException) when (texto == "")
- "Pegue a
FormatExceptionquando o texto estiver vazio." Para"", as duas partes combinam, e estecatchroda. - catch (FormatException)
- Para
"doze", o filtro de cima deufalse, então o C# continua procurando e chega neste, que pega qualquerFormatException. Doiscatchdo mesmo tipo são permitidos quando o primeiro tem filtro. - catch (Exception e) when (e is OverflowException or ArgumentNullException)
- Um
catchpara dois tipos.e is OverflowExceptionpergunta "a exceção é umaOverflowException?", e oor("ou") junta as duas perguntas (aula Pattern Matching). As outras exceções passam direto. - e.GetType().Name
- O nome da classe da exceção, sem o namespace:
OverflowException.
O C# não tem o multi-catch com barra (catch (A | B e)). A forma do C# é a do último catch acima: catch (Exception e) when (e is A or B).
Um detalhe que diferencia o filtro de um if dentro do catch: se a condição do when dá false, é como se aquele catch não existisse, e o C# continua procurando outra rede, nos catch de baixo ou em quem chamou o método. Com um if lá dentro, a exceção já teria sido capturada.
Com filtro, a regra da ordem também muda: um catch (Exception e) when (…) pode vir antes de um catch (FormatException), porque o filtro pode deixar a exceção passar. O compilador aceita; cabe a você pensar na ordem.
Missão: o programa responde "" não é um número. para o texto vazio, o que fica estranho. Acrescente, antes do catch que já existe, um catch (FormatException) when (…) que, para o texto vazio, mostre Você não digitou nada.
string[] entradas = ["12", "", "doze"];
foreach (string texto in entradas)
{
try
{
int quantidade = int.Parse(texto);
Console.WriteLine($"Quantidade: {quantidade}");
}
catch (FormatException)
{
Console.WriteLine($"\"{texto}\" não é um número.");
}
}
string[] entradas = ["12", "", "doze"];
foreach (string texto in entradas)
{
try
{
int quantidade = int.Parse(texto);
Console.WriteLine($"Quantidade: {quantidade}");
}
catch (FormatException) when (texto == "")
{
Console.WriteLine("Você não digitou nada.");
}
catch (FormatException)
{
Console.WriteLine($"\"{texto}\" não é um número.");
}
}
O que fazer com a exceção capturada
A variável e é um objeto, com propriedades e métodos úteis. Veja os principais lado a lado:
try
{
int n = int.Parse("12x");
}
catch (FormatException e)
{
Console.WriteLine($"Message: {e.Message}");
Console.WriteLine($"Tipo: {e.GetType().Name}");
Console.WriteLine($"Completo: {e.GetType().FullName}");
Console.WriteLine("--- ToString() ---");
Console.WriteLine(e.ToString());
Console.WriteLine("--- o programa continua ---");
}
$ dotnet run info.cs Message: The input string '12x' was not in a correct format. Tipo: FormatException Completo: System.FormatException --- ToString() --- System.FormatException: The input string '12x' was not in a correct format. at System.Number.ThrowFormatException[TChar](ReadOnlySpan`1 value) at System.Int32.Parse(String s) at Program.<Main>$(String[] args) in /home/ana/cs/info.cs:line 3 --- o programa continua ---
| Membro | Devolve | Quando usar |
|---|---|---|
e.Message | Só a mensagem: The input string '12x' was not in a correct format. | Para mostrar um detalhe do problema. Em inglês: para o usuário final, prefira uma frase sua. |
e.GetType().Name | Só o nome da classe: FormatException | Mensagens curtas. |
e.GetType().FullName | Com o namespace: System.FormatException | Para registrar sem ambiguidade. |
e.ToString() | Tipo, mensagem e o stack trace, igual ao relatório de uma exceção não tratada | Para registrar o problema completo num arquivo de log, enquanto você desenvolve. |
e.StackTrace | Só as linhas at … | Raramente sozinho: o ToString() já inclui. |
Repare: o ToString() mostra o mesmo relatório da exceção não tratada, só que sem o Unhandled exception. na frente, e o programa continua. Para mandar esse relatório para a saída de erro, como o .NET faz, use Console.Error.WriteLine(e).
A exceção sobe pelos métodos
O try não precisa estar no mesmo método da linha que lança. Uma exceção não tratada dentro de um método sobe para quem o chamou, depois para quem chamou esse, e assim por diante, degrau por degrau da pilha, até encontrar um catch que combine. É o mesmo caminho do stack trace, de cima para baixo:
try
{
Console.WriteLine(Calculo.Media(30, 0));
}
catch (DivideByZeroException e)
{
Console.WriteLine($"Não deu para calcular a média: {e.Message}");
}
Console.WriteLine("Fim");
static class Calculo
{
public static int Dividir(int a, int b) => a / b;
public static double Media(int soma, int quantidade) => Dividir(soma, quantidade);
}
$ dotnet run sobe.cs Não deu para calcular a média: Attempted to divide by zero. Fim
A exceção nasceu no Dividir, que não tem try. Subiu para o Media, que também não tem. Subiu para as instruções do topo, onde o catch (DivideByZeroException) a pegou. Se ninguém no caminho tivesse um catch adequado, ela chegaria ao fundo da pilha e o programa terminaria com 134. Isso permite tratar o problema onde faz sentido: o Dividir não sabe o que mostrar ao usuário; quem chamou sabe.
Um uso clássico: pedir de novo até acertar
Juntando try/catch com um while, o programa insiste até a pessoa digitar um número válido (a leitura do teclado está na aula Console.ReadLine):
int idade = -1;
while (idade < 0)
{
Console.Write("Sua idade: ");
string linha = Console.ReadLine() ?? "";
try
{
idade = int.Parse(linha);
}
catch (FormatException)
{
Console.WriteLine($"\"{linha}\" não é um número. Tente de novo.");
}
}
Console.WriteLine($"Ano que vem você terá {idade + 1} anos.");
$ dotnet run repete.cs Sua idade: vinte "vinte" não é um número. Tente de novo. Sua idade: 29 Ano que vem você terá 30 anos.
A idade começa em -1 (um valor impossível) para o laço rodar pelo menos uma vez. Se o int.Parse falhar, a atribuição idade = … não acontece, a idade continua -1 e o laço repete. O ?? "" troca um null (fim da entrada) por texto vazio (aula Operadores ??, ?. e ??=).
O mesmo programa com TryParse
Para entrada do usuário, a forma preferida em C# dispensa a exceção:
int idade;
Console.Write("Sua idade: ");
while (!int.TryParse(Console.ReadLine(), out idade))
{
Console.Write("Isso não é um número. Sua idade: ");
}
Console.WriteLine($"Ano que vem você terá {idade + 1} anos.");
$ dotnet run repete2.cs Sua idade: vinte Isso não é um número. Sua idade: 29 Ano que vem você terá 30 anos.
Mais curto, sem try e sem o valor -1 inventado. Lançar e capturar uma exceção dá mais trabalho ao computador do que um if; num laço que lê milhares de linhas de um arquivo, a diferença aparece. Guarde o try/catch para o que não dá para checar antes.
Não engula exceções
"Engolir" uma exceção é capturá-la e não fazer nada. O compilador aceita, o programa não quebra… e é exatamente esse o problema. A Ana somou os preços de uma lista:
string[] precos = ["10", "25", "sete e cinquenta", "8"];
decimal total = 0;
foreach (string p in precos)
{
try
{
total += decimal.Parse(p);
}
catch (FormatException)
{
}
}
Console.WriteLine($"Total: {total}");
$ dotnet run engole.cs Total: 43
O total certo seria 50,50. O "sete e cinquenta" lançou FormatException, o catch vazio "engoliu", e o programa mostrou um número errado sem avisar ninguém. Um programa que quebra é chato; um programa que dá a resposta errada em silêncio é muito pior, porque ninguém percebe.
Se você capturou, faça pelo menos uma destas coisas:
- Avise:
Console.WriteLine($"Preço inválido ignorado: {p}"); - Corrija: use um valor padrão que faça sentido, ou peça de novo.
- Registre:
Console.Error.WriteLine(e);, com o relatório completo. - Não capture: se você não sabe o que fazer com a exceção ali, deixe-a subir para quem sabe, ou relance com
throw;(aula throw).
Formas diferentes, qual usar
| Situação | Forma |
|---|---|
| Um tipo de problema, uma resposta | try { … } catch (TipoException) { … } |
| Precisa da mensagem ou do relatório | catch (TipoException e) e use e.Message ou e.ToString() |
| Vários problemas, respostas diferentes | Vários catch, do mais específico ao mais geral |
| Vários tipos, a mesma resposta | catch (Exception e) when (e is A or B), ou o catch da mãe comum (ArithmeticException) |
| Mesmo tipo, respostas diferentes conforme a situação | Filtro: catch (TipoException) when (condição) |
| Algo que precisa rodar com ou sem exceção | finally (próxima aula) ou using (using e IDisposable) |
| Você não sabe tratar ali | Não capture; deixe subir |
Dá para evitar com um if ou TryParse | Prefira o if/TryParse: if (provas != 0) é mais claro que capturar DivideByZeroException |
| Registrar tudo o que escapou, no topo do programa | Um único catch (Exception e) que registra e.ToString() |
Quando dá errado
try
try
{
int idade = int.Parse("29");
}
catch (FormatException)
{
Console.WriteLine("Não é número");
}
Console.WriteLine($"Idade: {idade}");
$ dotnet run escopo.cs /home/ana/cs/escopo.cs(9,29): error CS0103: O nome "idade" não existe no contexto atual Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O que aconteceu: as chaves do try são um bloco, e uma variável criada dentro de um bloco só existe lá dentro (aula Escopo). Na linha 9, idade já não existe.
Como consertar: declare a variável antes do try e só faça a atribuição dentro dele, ou coloque o WriteLine dentro do try. Mas cuidado com o próximo erro.
int idade;
try
{
idade = int.Parse("29");
}
catch (FormatException)
{
Console.WriteLine("Não é número");
}
Console.WriteLine($"Idade: {idade}");
$ dotnet run escopo2.cs /home/ana/cs/escopo2.cs(10,29): error CS0165: Uso de variável local não atribuída "idade" Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O que aconteceu: agora a variável existe, mas o compilador pensou no caminho com exceção: se o int.Parse falhar, a atribuição não acontece, o catch roda, e a linha 10 usaria uma idade sem valor. O C# não deixa.
Como consertar: dê um valor inicial na declaração (int idade = 0;), ou dê um valor também dentro do catch.
catch da mãe antes da filha
O que aconteceu: um catch de uma classe mãe (Exception, ArithmeticException) veio antes do catch de uma filha, sem filtro. O de baixo nunca seria usado.
Como consertar: coloque os catch do mais específico para o mais geral.
O que aconteceu: você escreveu catch (FormatException e) e não usou o e.
Como consertar: se não precisa do objeto, tire o nome: catch (FormatException). Se precisa, use-o (e.Message).
catch existe, mas o programa quebrou mesmo assim
O que aconteceu: a exceção lançada não é do tipo do catch (nem filha dele), ou o filtro when deu false. Também pode ser que a linha que lançou esteja fora do try.
Como consertar: leia a primeira linha do relatório: o tipo que aparece ali é o que você precisa capturar. Confira o número da linha: ela está entre as chaves do try?
O que aconteceu: provavelmente há um catch vazio engolindo exceções. Procure por catch seguido de { } no código.
Como consertar: coloque pelo menos um aviso ou Console.Error.WriteLine(e) dentro dele e rode de novo.
Referência
| Sintaxe | O que faz |
|---|---|
try { … } | Bloco vigiado: se algo lança exceção, o resto dele é pulado |
catch (Tipo) { … } | Roda se o try lançou Tipo ou uma filha dele |
catch (Tipo e) { … } | O mesmo, com o objeto da exceção na variável e |
catch { … } | Pega qualquer exceção, sem o objeto (evite) |
catch (A) { … } catch (B) { … } | Vários tratamentos; o primeiro que combina é usado |
catch (Tipo) when (condição) { … } | Filtro: só pega se a condição for true C# 6 |
catch (Exception e) when (e is A or B) | O mesmo tratamento para dois tipos |
e.Message | A mensagem |
e.GetType().Name | O nome da classe da exceção |
e.ToString() | Tipo + mensagem + stack trace |
Console.Error.WriteLine(e) | Escreve o relatório na saída de erro e continua |
CS0160 | catch de filha depois do da mãe |
CS0165 | Variável usada depois do try pode estar sem valor |
CS0168 (aviso) | A variável da exceção não é usada |
Exercícios
1. A palavra que falta
Complete para capturar o problema do int.Parse:
2. Só a mensagem
Complete para mostrar só a mensagem da exceção:
3. O filtro
Complete com a palavra que acrescenta uma condição ao catch:
4. O que este trecho mostra?
try
{
Console.WriteLine("A");
int x = int.Parse("x");
Console.WriteLine("B");
}
catch (FormatException)
{
Console.WriteLine("C");
}
Console.WriteLine("D");
5. Um try lança DivideByZeroException, e o único catch é catch (FormatException). O que acontece?
6. Qual ordem de catch (sem filtros) compila?
Missão final: o programa pede a idade até receber um número, mas quebra na primeira resposta (vinte). Proteja o int.Parse com try e um catch (FormatException) que mostre "…" não é um número. Tente de novo.. A entrada já vem preenchida: vinte e depois 29.
int idade = -1;
while (idade < 0)
{
Console.Write("Sua idade: ");
string linha = Console.ReadLine() ?? "";
idade = int.Parse(linha);
}
Console.WriteLine($"Ano que vem você terá {idade + 1} anos.");
int idade = -1;
while (idade < 0)
{
Console.Write("Sua idade: ");
string linha = Console.ReadLine() ?? "";
try
{
idade = int.Parse(linha);
}
catch (FormatException)
{
Console.WriteLine($"\"{linha}\" não é um número. Tente de novo.");
}
}
Console.WriteLine($"Ano que vem você terá {idade + 1} anos.");
Resumo
trymarca o código que pode lançar exceção;catch (Tipo)diz o que fazer se ela vier. Comcatch (Tipo e), você recebe o objeto da exceção.- Quando uma linha do
trylança, o resto dotryé pulado, ocatchroda e o programa continua depois do bloco, com código de saída 0. - O
catchpega o tipo pedido e as classes filhas; outros tipos passam direto e sobem. - Vários
catch: do mais específico ao mais geral, senãoCS0160. - Filtro
when (condição): ocatchsó pega se a condição for verdadeira.when (e is A or B)substitui o multi-catch de outras linguagens. e.Messagedá a mensagem;e.ToString(), o relatório completo.- A exceção sobe pelos métodos até achar um
catch: trate onde faz sentido. - Para entrada do usuário,
TryParseé melhor queParse+catch. Nunca deixe umcatchvazio.