Todo programador, por mais cuidadoso, tropeça em certos erros com frequência — armadilhas que a linguagem e a natureza da programação armam para os desavisados. Muitas delas você provavelmente já encontrou ao longo do curso, talvez perdendo minutos ou horas até descobrir a causa. Esta aula reúne as mais comuns num só lugar, explicando por que acontecem e como evitá-las ou reconhecê-las rapidamente. Considere-a um "mapa dos perigos": conhecer de antemão onde ficam as ciladas o pouparia de muitas frustrações e, mais importante, treinará seu olho para diagnosticar problemas depressa. Nenhuma dessas armadilhas é nova para você em conceito — todas tocam temas que estudamos —, mas vê-las reunidas e nomeadas como armadilhas dá a você uma vantagem prática valiosa. Um bom programador não é quem nunca erra, mas quem reconhece e corrige seus erros com rapidez.
Armadilha 1: confundir atribuição (=) com comparação (==)
Esta é talvez a confusão mais clássica de todas, e vale reforçá-la. O sinal = (um só) atribui — guarda um valor numa variável. O sinal == (dois) compara — verifica se dois valores são iguais, produzindo verdadeiro ou falso.
int idade = 30; // um '=': ATRIBUI 30 a idade
if (idade == 30) // dois '==': COMPARA se idade é 30
O engano é escrever if (idade = 30) (com um só) querendo comparar. Felizmente, na maioria dos casos, o C# o protege: como a condição de um if precisa ser um verdadeiro/falso, e idade = 30 produz um número (não um bool), o compilador acusa erro. Mas o hábito de distinguir "um igual guarda, dois iguais comparam" evita a confusão na origem. Sempre que for comparar, use ==; sempre que for guardar, use =.
Armadilha 2: a contagem que começa em zero
Índices de coleções (arrays, listas) começam em zero, não em um. O primeiro elemento está na posição 0, e o último está na posição Tamanho - 1, não Tamanho.
int[] numeros = { 10, 20, 30 }; // três elementos: posições 0, 1, 2
Console.WriteLine(numeros[0]); // 10 — o PRIMEIRO
// Console.WriteLine(numeros[3]); // QUEBRA! posição 3 não existe (índices vão de 0 a 2)
O erro típico é tentar acessar numeros[3] num array de três elementos, pensando no "terceiro" ou esquecendo que a contagem começa do zero — o que quebra o programa com um erro de "índice fora do intervalo". A regra a fixar: num conjunto de N elementos, os índices válidos vão de 0 a N-1. Quando encontrar esse erro, sua primeira suspeita deve ser um acesso que ultrapassou o limite por causa da contagem a partir do zero.
Armadilha 3: usar algo que é nulo
Tentar acessar uma property ou método de uma variável que é null quebra o programa com a temida exceção de referência nula. É o erro de tratar o "nada" como se fosse "algo".
string texto = null;
// Console.WriteLine(texto.Length); // QUEBRA! 'texto' é null, não há tamanho
Este é um dos erros mais comuns em tempo de execução. A prevenção: verifique se algo pode ser nulo antes de usá-lo (com if (x != null), ou com os operadores ?. e ??), e preste atenção aos avisos do compilador moderno sobre possíveis nulos. Quando encontrar uma "NullReferenceException", a pergunta a fazer é: "qual variável, naquela linha, estava valendo null quando eu tentei usá-la?".
Armadilha 4: a divisão inteira inesperada
Quando você divide dois números inteiros, o C# faz divisão inteira, descartando a parte fracionária — o que surpreende quem espera um resultado com casas decimais.
int a = 10, b = 3;
double resultado = a / b; // resultado é 3.0, não 3.333! (a/b é feito como inteiros)
O engano é esperar 3.333 e obter 3, porque a divisão aconteceu entre inteiros antes de o resultado ser guardado no double. A prevenção: se você quer casas decimais, garanta que ao menos um dos números seja de tipo decimal — por exemplo, (double)a / b, convertendo a para double antes da divisão. Quando um cálculo com divisão der um resultado "redondo" inesperado, suspeite da divisão inteira.
Armadilha 5: modificar uma coleção enquanto a percorre
Tentar adicionar ou remover elementos de uma coleção enquanto você a percorre com foreach causa um erro de execução. O foreach não permite que a coleção mude sob seus pés durante a iteração.
List<int> numeros = new() { 1, 2, 3, 4 };
// foreach (int n in numeros)
// if (n % 2 == 0) numeros.Remove(n); // QUEBRA! modificando durante o foreach
A prevenção: se você precisa remover itens enquanto examina uma coleção, use um laço for com índice (com cuidado), ou colete os itens a remover numa lista separada e remova-os depois do foreach, ou use métodos apropriados como RemoveAll. Reconheça o erro "coleção foi modificada" como sinal desta armadilha.
Armadilha 6: esquecer que objetos são compartilhados por referência
Como vimos na aula de valor e referência, atribuir uma variável de objeto a outra não copia o objeto — copia a referência, fazendo as duas apontarem para o mesmo objeto. Alterar um afeta o outro.
var lista1 = new List<int> { 1, 2, 3 };
var lista2 = lista1; // NÃO é uma cópia! lista2 aponta para a MESMA lista
lista2.Add(4);
Console.WriteLine(lista1.Count); // 4! lista1 mudou também
O engano é achar que lista2 é uma cópia independente e surpreender-se quando alterar lista2 muda lista1. A prevenção: lembre que objetos são compartilhados por referência; se precisar de uma cópia independente, crie um novo objeto explicitamente. Quando uma alteração "inexplicável" aparecer num objeto que você não tocou diretamente, suspeite de referências compartilhadas.
Um princípio geral: leia a mensagem de erro
Reunidas essas armadilhas, vale reforçar o princípio que as atravessa e que já enfatizamos na aula de depuração: a mensagem de erro é sua aliada. Cada uma dessas armadilhas, quando cai nela, produz uma mensagem — "índice fora do intervalo", "referência nula", "coleção modificada" — que aponta diretamente para a natureza do problema. O programador experiente não decorou soluções; ele aprendeu a ler as mensagens e a associá-las às causas típicas, o que esta aula acabou de ajudá-lo a fazer. Quando um programa quebrar, não entre em pânico nem mude coisas ao acaso: leia a mensagem, identifique o tipo de erro, e cruze-o com o mapa de armadilhas que você agora conhece. Na maioria das vezes, isso o leva direto à causa. Diagnosticar bem é metade de corrigir, e você acabou de ganhar um bom mapa para diagnosticar.
Seis armadilhas ficaram mapeadas, e todas elas compartilham uma característica: são erros que qualquer programador comete, não sinais de despreparo. Confundir atribuição com comparação, esquecer que a contagem começa em zero, usar algo nulo, ser surpreendido pela divisão inteira, modificar uma coleção durante o percurso e ignorar que objetos são compartilhados por referência.
O valor de conhecê-las antecipadamente está no tempo de diagnóstico: um sintoma que levaria uma tarde de perplexidade passa a ser reconhecido em segundos. E o princípio geral vale mais que a lista inteira — leia a mensagem de erro. Ela quase sempre aponta o arquivo, a linha e a natureza do problema.
Fontes e leituras recomendadas
- Erros comuns em C# — referência das mensagens de erro do compilador e suas causas.
- IndexOutOfRangeException — o erro de índice fora do intervalo, da armadilha 2.
- NullReferenceException — o erro de referência nula, da armadilha 3, e como evitá-lo.
- Divisão de inteiros — o comportamento da divisão inteira, da armadilha 4.
- Modificar coleções durante a iteração — o erro da armadilha 5 e como evitá-lo.
Exercícios
Exercício 1
Provoque deliberadamente a armadilha 2 (índice fora do intervalo): crie um array de três elementos e tente acessar a posição 3. Leia a mensagem de erro e explique como ela aponta para a causa. Depois, corrija o acesso para uma posição válida.
Ver resposta
✓ Resposta: Exemplo:
int[] arr = { 10, 20, 30 };
// Console.WriteLine(arr[3]); // erro
Console.WriteLine(arr[2]); // correto: 30 (última posição válida)
Ao acessar arr[3], o programa quebra com uma mensagem como "Index was outside the bounds of the array" ("índice estava fora dos limites do array"), que aponta diretamente para a causa: tentou-se acessar uma posição que não existe. Como o array tem três elementos, as posições válidas vão de 0 a 2, e a correção é usar um índice dentro desse intervalo (por exemplo, arr[2] para o último elemento).
Exercício 2
Escreva um trecho que caia na armadilha 4 (divisão inteira): divida dois inteiros esperando um resultado decimal, e observe o resultado "redondo". Depois, corrija-o para obter o resultado com casas decimais, e explique o que sua correção fez.
Ver resposta
✓ Resposta: Exemplo:
int a = 7, b = 2;
double errado = a / b; // 3.0 (divisão inteira: 7/2 = 3, descartando a fração)
double certo = (double)a / b; // 3.5
O primeiro resultado é 3.0 porque a / b é calculado entre inteiros antes de ser guardado no double, descartando a parte fracionária. A correção (double)a / b converte a para double antes da divisão, fazendo o C# realizar uma divisão decimal (não inteira), o que preserva a fração e produz 3.5. Basta um dos operandos ser decimal para a divisão inteira ser evitada.
Exercício 3
Reproduza a armadilha 6 (referência compartilhada): crie uma List, atribua-a a outra variável, altere a segunda, e mostre que a primeira mudou também. Explique por que isso acontece e como você obteria uma cópia de verdade.
Ver resposta
✓ Resposta: Exemplo:
var listaA = new List<int> { 1, 2, 3 };
var listaB = listaA; // não copia; aponta para a mesma lista
listaB.Add(4);
Console.WriteLine(listaA.Count); // 4 — mudou também
Isso acontece porque List é um tipo por referência: listaB = listaA copia apenas a referência (o endereço), fazendo listaA e listaB apontarem para o mesmo objeto na memória; alterar por uma afeta o que a outra vê, pois há uma só lista. Para obter uma cópia de verdade, seria preciso criar uma nova lista com os mesmos elementos (por exemplo, var copia = new List<int>(listaA);), gerando um objeto independente.
Exercício 4
Para cada mensagem de erro a seguir, diga a qual armadilha desta aula ela provavelmente corresponde e qual seria sua primeira suspeita sobre a causa: (a) "Index was outside the bounds of the array"; (b) "Object reference not set to an instance of an object" (a mensagem da referência nula); (c) "Collection was modified; enumeration operation may not execute".
Ver resposta
✓ Resposta: (a) "Index was outside the bounds of the array" → armadilha 2 (contagem/índice); primeira suspeita: um acesso a uma posição de array ou lista que ultrapassou o limite, provavelmente por esquecer que a contagem começa em zero (o último índice é Tamanho - 1). (b) "Object reference not set to an instance of an object" → armadilha 3 (nulo); primeira suspeita: alguma variável estava valendo null naquela linha quando se tentou acessar uma property ou método dela. (c) "Collection was modified; enumeration operation may not execute" → armadilha 5 (modificar durante o foreach); primeira suspeita: um Add ou Remove na coleção dentro de um foreach que a percorre.
Exercício 5
Sem código: a aula afirma que "diagnosticar bem é metade de corrigir". Explique como conhecer as armadilhas comuns e saber ler as mensagens de erro torna você mais rápido em resolver problemas, em comparação com mudar o código ao acaso quando algo quebra.
Ver resposta
✓ Resposta: "Diagnosticar bem é metade de corrigir" significa que a maior parte do esforço de resolver um bug está em identificar a causa; uma vez conhecida a causa, a correção costuma ser simples. Conhecer as armadilhas comuns e saber ler as mensagens de erro torna você mais rápido porque, quando um programa quebra, você associa imediatamente a mensagem ("índice fora do intervalo", "referência nula") ao tipo de problema e à sua causa típica, indo direto ao ponto — sabe onde olhar e o que suspeitar. Isso contrasta com mudar o código ao acaso: sem entender a causa, você tenta soluções cegas, uma após outra, o que é lento, frustrante, e pode até introduzir novos erros sem resolver o original. Diagnosticar a partir da mensagem transforma a correção de um processo de tentativa e erro num processo dirigido e eficiente.