C# Exceções
Uma exceção é o jeito de o .NET dizer "não consigo continuar": o índice não existe, o texto não é número, o arquivo sumiu. Se ninguém trata, o programa para e mostra a mensagem. Esta página lista as exceções que você mais vai ver, cada uma com o código que a provoca, a mensagem real, o que ela quer dizer e como evitar. Para aprender com calma, veja Exceções, try e catch e throw.
- Copie um pedaço da mensagem que apareceu para você (por exemplo,
was not in a correct format) e procure com Ctrl+F. - No Linux, as mensagens das exceções saem em inglês, mesmo com o sistema em português (o .NET não traz a tradução delas). As do compilador (
CSxxxx) saem em português: estão em Erros do Compilador. - Todas foram provocadas de verdade, no
dotnet10.0.112 da máquina da Ana (pasta~/cs/exc), e conferidas no editor do guia. Onde o editor mostra outra coisa, a tabela diz.
Atalhos: Ler uma exceção · As exceções mais comuns · A família · O que tem dentro · Tratar · Lançar · Evitar em vez de tratar · Experimente
Como ler uma exceção não tratada
int[] notas = [7, 9, 5];
Console.WriteLine("Antes");
Console.WriteLine(notas[3]);
Console.WriteLine("Depois");
$ dotnet run naotratada.cs Antes Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array. at Program.<Main>$(String[] args) in /home/ana/cs/exc/naotratada.cs:line 3 $ echo $? 134
| Pedaço | O que quer dizer |
|---|---|
Unhandled exception. | "Exceção não tratada": nenhum catch pegou, e o programa parou ali. O Depois nunca apareceu. |
System.IndexOutOfRangeException | O tipo da exceção, com o namespace. É o que você escreve num catch. |
Index was outside the bounds of the array. | A mensagem (a propriedade Message): "o índice estava fora dos limites do array". |
at Program.<Main>$(String[] args) | O rastro (stack trace): os métodos em andamento, do mais recente (em cima) para o mais antigo. <Main>$ é o nome que o compilador dá às instruções de nível superior. |
in /home/ana/cs/exc/naotratada.cs:line 3 | Arquivo e linha. Leia o rastro de cima para baixo até achar o primeiro arquivo seu: as linhas de System.… são de dentro do .NET. |
134 | O código de saída de um programa que caiu por exceção, no Linux (é o sinal SIGABRT: 128 + 6). Um programa que termina bem sai com 0. |
As exceções mais comuns
Este programa provoca 30 exceções, uma por linha, e mostra o tipo e a mensagem de cada uma. Antes de rodar, a Ana criou um arquivo só de leitura, para a linha 20:
// catalogo.cs — provoca cada exceção e mostra o tipo e a mensagem
using System.Collections;
string? nulo = null;
int[] v = [1, 2, 3];
List<int> lista = [1, 2, 3];
Dictionary<string, int> d = new() { ["a"] = 1 };
object texto = "x";
int zero = 0;
int? semValor = null;
string grande = "99999999999";
string titulo = " ";
int quantidade = -5;
Provocar(1, () => _ = nulo!.Length);
Provocar(2, () => _ = v[3]);
Provocar(3, () => _ = lista[3]);
Provocar(4, () => _ = d["b"]);
Provocar(5, () => d.Add("a", 2));
Provocar(6, () => _ = int.Parse("abc"));
Provocar(7, () => _ = int.Parse(grande));
Provocar(8, () => _ = checked(int.MaxValue + zero + 1));
Provocar(9, () => _ = 10 / zero);
Provocar(10, () => _ = (int)texto);
Provocar(11, () => _ = new List<int>().First());
Provocar(12, () => { foreach (int x in lista) lista.Add(x); });
Provocar(13, () => _ = semValor!.Value);
Provocar(14, () => _ = int.Parse(nulo!));
Provocar(15, () => ArgumentNullException.ThrowIfNull(nulo));
Provocar(16, () => ArgumentException.ThrowIfNullOrWhiteSpace(titulo));
Provocar(17, () => ArgumentOutOfRangeException.ThrowIfNegative(quantidade));
Provocar(18, () => _ = File.ReadAllText("nao-existe.txt"));
Provocar(19, () => _ = File.ReadAllText("pasta-que-nao-existe/a.txt"));
Provocar(20, () => File.WriteAllText("somente-leitura.txt", "oi"));
Provocar(21, () => ((IList)v).Add(4));
Provocar(22, () => { var w = new StringWriter(); w.Dispose(); w.Write("oi"); });
Provocar(23, () => throw new NotImplementedException());
Provocar(24, () => _ = "Ana".Substring(5));
Provocar(25, () => _ = new string('x', -1));
Provocar(26, () => _ = DateTime.Parse("31/02/2026"));
Provocar(27, () => _ = Enum.Parse<DayOfWeek>("Segunda"));
Provocar(28, () => { var c = new CancellationTokenSource(); c.Cancel(); c.Token.ThrowIfCancellationRequested(); });
Provocar(29, () => _ = decimal.Parse("1,5,0"));
Provocar(30, () => _ = Convert.ToInt32("12a"));
static void Provocar(int n, Action acao)
{
try
{
acao();
Console.WriteLine($"{n,2} (nenhuma exceção)");
}
catch (Exception e)
{
Console.WriteLine($"{n,2} {e.GetType().FullName}: {e.Message}");
}
}
$ touch somente-leitura.txt
$ chmod 444 somente-leitura.txt
$ dotnet run catalogo.cs
1 System.NullReferenceException: Object reference not set to an instance of an object.
2 System.IndexOutOfRangeException: Index was outside the bounds of the array.
3 System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
4 System.Collections.Generic.KeyNotFoundException: The given key 'b' was not present in the dictionary.
5 System.ArgumentException: An item with the same key has already been added. Key: a
6 System.FormatException: The input string 'abc' was not in a correct format.
7 System.OverflowException: Value was either too large or too small for an Int32.
8 System.OverflowException: Arithmetic operation resulted in an overflow.
9 System.DivideByZeroException: Attempted to divide by zero.
10 System.InvalidCastException: Unable to cast object of type 'System.String' to type 'System.Int32'.
11 System.InvalidOperationException: Sequence contains no elements
12 System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
13 System.InvalidOperationException: Nullable object must have a value.
14 System.ArgumentNullException: Value cannot be null. (Parameter 's')
15 System.ArgumentNullException: Value cannot be null. (Parameter 'nulo')
16 System.ArgumentException: The value cannot be an empty string or composed entirely of whitespace. (Parameter 'titulo')
17 System.ArgumentOutOfRangeException: quantidade ('-5') must be a non-negative value. (Parameter 'quantidade')
Actual value was -5.
18 System.IO.FileNotFoundException: Could not find file '/home/ana/cs/exc/nao-existe.txt'.
19 System.IO.DirectoryNotFoundException: Could not find a part of the path '/home/ana/cs/exc/pasta-que-nao-existe/a.txt'.
20 System.UnauthorizedAccessException: Access to the path '/home/ana/cs/exc/somente-leitura.txt' is denied.
21 System.NotSupportedException: Collection was of a fixed size.
22 System.ObjectDisposedException: Cannot write to a closed TextWriter.
23 System.NotImplementedException: The method or operation is not implemented.
24 System.ArgumentOutOfRangeException: startIndex cannot be larger than length of string. (Parameter 'startIndex')
25 System.ArgumentOutOfRangeException: count ('-1') must be a non-negative value. (Parameter 'count')
Actual value was -1.
26 System.FormatException: String '31/02/2026' was not recognized as a valid DateTime.
27 System.ArgumentException: Requested value 'Segunda' was not found.
28 System.OperationCanceledException: The operation was canceled.
29 System.FormatException: The input string '1,5,0' was not in a correct format.
30 System.FormatException: The input string '12a' was not in a correct format.
Dados e valores
| # | Exceção | Quando acontece | Mensagem real | Como evitar | Aula |
|---|---|---|---|---|---|
| 1 | NullReferenceException | usar . num valor null | Object reference not set to an instance of an object. | confira is null, use ?. e ??, e leve a sério o aviso CS8602 | Referências Anuláveis |
| 13 | InvalidOperationException | .Value de um int? sem valor | Nullable object must have a value. | HasValue, ?? padrão ou GetValueOrDefault() | null |
| 6 | FormatException | int.Parse("abc") | The input string 'abc' was not in a correct format. | int.TryParse com o que o usuário digita | Parse e TryParse |
| 29, 30 | FormatException | decimal.Parse("1,5,0"), Convert.ToInt32("12a") | The input string '1,5,0' was not in a correct format. | decimal.TryParse; atenção à cultura (vírgula ou ponto) | Parse e TryParse |
| 26 | FormatException | DateTime.Parse("31/02/2026") | String '31/02/2026' was not recognized as a valid DateTime. | DateTime.TryParse (fevereiro não tem dia 31) | Datas e Horas |
| 7 | OverflowException | int.Parse("99999999999") | Value was either too large or too small for an Int32. | TryParse (devolve false), ou long | Inteiros |
| 8 | OverflowException | conta que estoura dentro de checked | Arithmetic operation resulted in an overflow. | um tipo maior (long, decimal) | Tipos e Limites |
| 9 | DivideByZeroException | inteiro dividido por zero | Attempted to divide by zero. | confira o divisor antes (com double, não há exceção: dá ∞) | Operadores Aritméticos |
| 10 | InvalidCastException | (int)texto com object texto = "x" | lab: Unable to cast object of type 'System.String' to type 'System.Int32'.editor: Specified cast is not valid. | teste com is (if (o is int n)) ou use as | Pattern Matching |
| 27 | ArgumentException | Enum.Parse<DayOfWeek>("Segunda") | Requested value 'Segunda' was not found. | Enum.TryParse | Enums |
Arrays, listas, textos e coleções
| # | Exceção | Quando acontece | Mensagem real | Como evitar | Aula |
|---|---|---|---|---|---|
| 2 | IndexOutOfRangeException | v[3] num array de 3 (ou "Ana"[3]) | Index was outside the bounds of the array. | as posições vão de 0 a Length - 1; percorra com foreach | Arrays |
| 3 | ArgumentOutOfRangeException | lista[3] numa List de 3 | Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index') | de 0 a Count - 1 | List<T> |
| 24 | ArgumentOutOfRangeException | "Ana".Substring(5) | startIndex cannot be larger than length of string. (Parameter 'startIndex') | confira o Length | Métodos de String |
| 25 | ArgumentOutOfRangeException | new string('x', -1) | count ('-1') must be a non-negative value. (Parameter 'count') Actual value was -1. | não passe tamanhos negativos | string |
| 4 | KeyNotFoundException | d["b"] com a chave ausente | The given key 'b' was not present in the dictionary. | TryGetValue ou GetValueOrDefault | Dictionary |
| 5 | ArgumentException | d.Add("a", 2) com a chave já existente | An item with the same key has already been added. Key: a | d["a"] = 2 (troca) ou TryAdd | Dictionary |
| 11 | InvalidOperationException | First() numa sequência vazia | Sequence contains no elements | FirstOrDefault, ou Any() antes | LINQ |
| 12 | InvalidOperationException | mudar a lista dentro do foreach dela | Collection was modified; enumeration operation may not execute. | RemoveAll, ou percorra uma cópia (.ToList()) | foreach |
| 21 | NotSupportedException | Add num array (pela interface IList) | Collection was of a fixed size. | array tem tamanho fixo: use List | Arrays |
Outras de coleções e LINQ, com a mensagem: Queue empty., Stack empty., Sequence contains no matching element, Sequence contains more than one element (em Coleções e LINQ).
Argumentos (lançadas pelos seus métodos)
| # | Exceção | Quando acontece | Mensagem real | Aula |
|---|---|---|---|---|
| 14 | ArgumentNullException | int.Parse(null) | Value cannot be null. (Parameter 's') | Parse e TryParse |
| 15 | ArgumentNullException | ArgumentNullException.ThrowIfNull(nulo) .NET 6 | Value cannot be null. (Parameter 'nulo') | throw |
| 16 | ArgumentException | ArgumentException.ThrowIfNullOrWhiteSpace(titulo) .NET 8 | The value cannot be an empty string or composed entirely of whitespace. (Parameter 'titulo') | throw |
| 17 | ArgumentOutOfRangeException | ArgumentOutOfRangeException.ThrowIfNegative(quantidade) .NET 8 | quantidade ('-5') must be a non-negative value. (Parameter 'quantidade') Actual value was -5. | throw |
Os ThrowIf… descobrem sozinhos o nome da variável (o (Parameter 'titulo')). Há também ThrowIfNullOrEmpty, ThrowIfZero, ThrowIfNegativeOrZero, ThrowIfGreaterThan e ThrowIfLessThan.
Arquivos e recursos
| # | Exceção | Quando acontece | Mensagem real | Como evitar | Aula |
|---|---|---|---|---|---|
| 18 | FileNotFoundException | ler um arquivo que não existe | Could not find file '/home/ana/cs/exc/nao-existe.txt'. | File.Exists antes; a mensagem mostra o caminho completo em que o programa procurou | Arquivos |
| 19 | DirectoryNotFoundException | a pasta do caminho não existe | Could not find a part of the path '/home/ana/cs/exc/pasta-que-nao-existe/a.txt'. | Directory.CreateDirectory antes de gravar | Pastas e Caminhos |
| 20 | UnauthorizedAccessException | gravar num arquivo sem permissão | Access to the path '/home/ana/cs/exc/somente-leitura.txt' is denied. | grave na pasta da usuária; trate com try/catch | Arquivos |
| 22 | ObjectDisposedException | usar um StreamWriter depois de fechado | Cannot write to a closed TextWriter. | use o objeto só dentro do bloco do using | using e IDisposable |
| 28 | OperationCanceledException | uma tarefa cancelada com CancellationToken | The operation was canceled. | trate no catch: em geral, é o usuário que cancelou | Cancelamento |
| 23 | NotImplementedException | throw new NotImplementedException(): "ainda não escrevi isto" | The method or operation is not implemented. | escreva o método | Interfaces |
Todas as exceções de arquivo descendem de IOException: um catch (IOException) pega todas (menos a UnauthorizedAccessException, que é de outra família).
Stack overflow: a que não dá para tratar
static int Contar(int n) => Contar(n + 1) + 1; Console.WriteLine(Contar(0));
$ dotnet run recursao.cs Stack overflow. Repeated 261726 times: -------------------------------- at Program.<<Main>$>g__Contar|0_0(Int32) -------------------------------- at Program.<Main>$(System.String[]) $ echo $? 134
Uma recursão sem caso de parada enche a pilha de chamadas. O .NET encerra o programa na hora: nenhum catch pega uma StackOverflowException. O Repeated 261726 times é quantas vezes o Contar chegou a se chamar; o número muda um pouco de uma execução para outra (numa segunda vez, deu 261831). O editor do guia mostra a mesma mensagem e o mesmo código 134, mas com uma pilha menor: Repeated 999 times. Como consertar: toda recursão precisa de um caso que para sem chamar de novo.
A família das exceções
Toda exceção é uma classe que herda de Exception. Um catch pega a classe que você escreveu e todas as filhas:
Exception ├── SystemException │ ├── ArgumentException │ │ ├── ArgumentNullException │ │ └── ArgumentOutOfRangeException │ ├── ArithmeticException │ │ ├── DivideByZeroException │ │ └── OverflowException │ ├── FormatException │ ├── IndexOutOfRangeException │ ├── InvalidCastException │ ├── InvalidOperationException │ │ └── ObjectDisposedException │ ├── IO.IOException │ │ ├── IO.FileNotFoundException │ │ └── IO.DirectoryNotFoundException │ ├── Collections.Generic.KeyNotFoundException │ ├── NotSupportedException │ ├── NotImplementedException │ ├── NullReferenceException │ ├── OperationCanceledException │ └── UnauthorizedAccessException └── (as suas: class SaldoInsuficienteException : Exception)
Por isso, num try com vários catch, a mais específica vem primeiro: catch (FileNotFoundException) antes de catch (IOException), e catch (Exception) por último. Ao contrário, o compilador reclama com o erro CS0160 (veja Erros do Compilador). Para criar a sua, aula Exceções Próprias.
O que tem dentro de uma exceção
// propriedades.cs — o que um objeto Exception tem dentro
try
{
Carregar("config.txt");
}
catch (Exception e)
{
Console.WriteLine($"Tipo: {e.GetType().Name}");
Console.WriteLine($"Message: {e.Message}");
Console.WriteLine($"InnerException: {e.InnerException?.GetType().Name}");
Console.WriteLine($"InnerException.Message: {e.InnerException?.Message}");
Console.WriteLine("StackTrace:");
Console.WriteLine(e.StackTrace);
}
static void Carregar(string arquivo)
{
try
{
File.ReadAllText(arquivo);
}
catch (FileNotFoundException e)
{
throw new InvalidOperationException($"Não consegui carregar {arquivo}.", e);
}
}
$ dotnet run propriedades.cs Tipo: InvalidOperationException Message: Não consegui carregar config.txt. InnerException: FileNotFoundException InnerException.Message: Could not find file '/home/ana/cs/exc/config.txt'. StackTrace: at Program.<<Main>$>g__Carregar|0_0(String arquivo) in /home/ana/cs/exc/propriedades.cs:line 24 at Program.<Main>$(String[] args) in /home/ana/cs/exc/propriedades.cs:line 4
| Membro | O que é |
|---|---|
Message | a mensagem, para pessoas |
GetType().Name | o tipo (FileNotFoundException) |
StackTrace | o rastro, como texto |
InnerException | a exceção original, quando uma exceção foi "embrulhada" em outra (o segundo argumento do construtor, como no Carregar) |
ToString() | tudo junto: tipo, mensagem, exceção interna e rastro |
ParamName | só em ArgumentException e filhas: o nome do parâmetro culpado |
FileName | só em FileNotFoundException: o caminho do arquivo |
O nome estranho <<Main>$>g__Carregar|0_0 é como o compilador chama uma função local das instruções de nível superior. Leia só o Carregar.
Tratar: try, catch, when e finally
// trycatch.cs — tratar, filtrar, finally e relançar
foreach (string entrada in new[] { "10", "zero", "0", "99999999999" })
{
try
{
int n = int.Parse(entrada);
Console.WriteLine($"100 / {n} = {100 / n}");
}
catch (FormatException)
{
Console.WriteLine($"'{entrada}' não é número.");
}
catch (Exception e) when (e is DivideByZeroException or OverflowException)
{
Console.WriteLine($"'{entrada}': {e.GetType().Name}");
}
finally
{
Console.WriteLine(" (fim da tentativa)");
}
}
$ dotnet run trycatch.cs 100 / 10 = 10 (fim da tentativa) 'zero' não é número. (fim da tentativa) '0': DivideByZeroException (fim da tentativa) '99999999999': OverflowException (fim da tentativa)
| Forma | O que faz | Aula |
|---|---|---|
try { … } | o código que pode lançar | try e catch |
catch (FormatException) { … } | pega esse tipo (e as filhas); sem variável, quando não precisa da mensagem | |
catch (IOException e) { … e.Message … } | pega e dá um nome, para ler a mensagem | |
catch (Exception e) when (condição) C# 6 | só pega se a condição for verdadeira; senão, a exceção continua subindo | |
finally { … } | roda sempre: deu certo, deu exceção tratada, ou não tratada | finally |
using var recurso = …; | um finally automático que fecha o recurso | using e IDisposable |
Lançar: throw
| Forma | Quando usar | Aula |
|---|---|---|
throw new ArgumentException("…", nameof(param)); | o argumento recebido não serve | throw |
throw new InvalidOperationException("…"); | o objeto não está num estado que permite isso (sacar de uma conta encerrada) | |
ArgumentNullException.ThrowIfNull(x); | o jeito curto de recusar null .NET 6 | |
throw; (dentro do catch) | relança a mesma exceção, mantendo o rastro original. Não escreva throw e;, que apaga o rastro. | |
throw new MinhaException("…", e); | embrulha a original como InnerException (como no Carregar acima) | |
x ?? throw new … | throw como expressão C# 7: Nome = nome ?? throw new ArgumentNullException(nameof(nome)); | Operadores ?? |
Evitar em vez de tratar
Exceção é para o que é excepcional. Para o que acontece sempre (o usuário digita errado, a chave não existe), há métodos que perguntam antes, sem exceção nenhuma:
| Em vez de… | Que lança… | Use… |
|---|---|---|
int.Parse(texto) | FormatException, OverflowException | int.TryParse(texto, out int n) |
d[chave] | KeyNotFoundException | d.TryGetValue(chave, out var v) |
lista.First(…) | InvalidOperationException | lista.FirstOrDefault(…) e conferir o null |
fila.Dequeue() | InvalidOperationException | fila.TryDequeue(out var x) |
(int)objeto | InvalidCastException | if (objeto is int n) |
File.ReadAllText(caminho) sem saber se existe | FileNotFoundException | if (File.Exists(caminho)) (e ainda um try, porque o arquivo pode sumir entre as duas linhas) |
texto.Length com texto anulável | NullReferenceException | texto?.Length ?? 0 |
Experimente
Missão: a entrada já vem preenchida com vinte, e o programa cai com FormatException (código de saída 134). Troque o int.Parse por int.TryParse para ele mostrar Idade inválida. e terminar normalmente.
Console.Write("Sua idade: ");
string texto = Console.ReadLine() ?? "";
int idade = int.Parse(texto);
Console.WriteLine($"Ano que vem você terá {idade + 1}.");
Console.Write("Sua idade: ");
string texto = Console.ReadLine() ?? "";
if (int.TryParse(texto, out int idade))
{
Console.WriteLine($"Ano que vem você terá {idade + 1}.");
}
else
{
Console.WriteLine("Idade inválida.");
}
Experimente: descomente uma linha por vez e rode. Leia o tipo e a mensagem de cada exceção, depois conserte a linha.
int[] notas = [7, 9, 5];
string? apelido = null;
Dictionary<string, int> idades = new() { ["Ana"] = 29 };
// Console.WriteLine(notas[3]);
// Console.WriteLine(apelido!.Length);
// Console.WriteLine(idades["Bruno"]);
// Console.WriteLine(int.Parse("dez"));
// Console.WriteLine(new List<int>().First());
Console.WriteLine("Fim do programa.");
Resumo
- Uma exceção não tratada mostra
Unhandled exception., o tipo, a mensagem (em inglês) e o rastro, e o programa sai com o código 134. - Leia o rastro de cima para baixo até o primeiro arquivo seu: ali está a linha culpada.
- As campeãs:
NullReferenceException,IndexOutOfRangeException/ArgumentOutOfRangeException,FormatException,KeyNotFoundException,InvalidOperationExceptioneFileNotFoundException. - Prefira evitar:
TryParse,TryGetValue,FirstOrDefault,is,?.. - Para tratar:
try/catchdo mais específico para o mais geral,whenpara filtrar,finallyouusingpara liberar recursos,throw;para relançar. - Stack overflow não se trata: conserte a recursão.