Por que o C# reutiliza a variável no foreach? Explicação para iniciantes

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: o enigma que atormenta os iniciantes

\n\n

Imagine a seguinte situação: você está escrevendo um loop foreach simples em C#, salva uma expressão lambda ou inicia uma tarefa assíncrona dentro do loop, e de repente obtém um resultado completamente inesperado. Todas as variáveis que você, aparentemente, "capturou" cuidadosamente, de repente se referem ao último elemento da coleção. Familiar?

\n\n

No Stack Overflow, a pergunta "Is there a reason for C#'s reuse of the variable in a foreach?" acumulou milhares de visualizações e inúmeros comentários. Isso não é apenas um interesse acadêmico — é uma armadilha prática na qual até mesmo desenvolvedores experientes caem. Neste artigo, vamos entender por que o C# faz isso, como isso mudou com as versões da linguagem e, o mais importante, como evitar os erros associados.

\n\n

Vamos falar de forma simples, com exemplos de código, para que até mesmo um iniciante em C# entenda a essência do problema. Vamos lá!

\n\n

O cerne do problema: o que significa "reutilização de variável"?

\n\n

No loop foreach clássico em C# (antes da versão 5.0), a variável de iteração era declarada uma única vez para todo o loop, e não era criada novamente para cada iteração. Isso significa que, se você capturar essa variável em uma closure (por exemplo, em uma expressão lambda ou método anônimo), todas as closures se referirão à mesma variável.

\n\n

Vamos ver o exemplo clássico do problema:

\n\n
var actions = new List<Action>();\nvar numbers = new List<int> { 1, 2, 3, 4, 5 };\n\nforeach (var number in numbers)\n{\n    actions.Add(() => Console.WriteLine(number));\n}\n\n// Executando todas as ações\nforeach (var action in actions)\n{\n    action();\n}\n
\n\n

Pergunta: o que este código exibirá? Intuitivamente, parece que será 1, 2, 3, 4, 5. Mas, na verdade (antes do C# 5), ele exibiria 5, 5, 5, 5, 5.

\n\n

Por quê? Porque a variável number existe em uma única instância durante todo o loop. Quando o loop termina, seu valor é igual ao último elemento (5). Todas as lambdas capturaram a referência para esta variável, e não o seu valor no momento da criação.

\n\n

Analogia para entender

\n\n

Imagine que você tem uma caixa (variável) e coloca nela, um por um, diferentes objetos (valores). Você dá aos seus amigos (lambdas) não os objetos em si, mas a chave da caixa. Quando os amigos vêm buscar seus objetos, eles abrem a caixa e lá está — o último objeto que você colocou. É assim que funciona o foreach antigo.

\n\n

Contexto histórico: por que foi feito assim?

\n\n

Muitos iniciantes perguntam: "Isso é um bug! Por que fazer isso?" Na verdade, foi uma decisão consciente dos desenvolvedores do C#, baseada em desempenho e simplicidade de compilação.

\n\n
    \n
  • Desempenho: Criar uma nova variável a cada iteração é uma alocação adicional de memória. Para a maioria dos loops simples, isso não é crítico, mas em sistemas de alta carga, cada micro-otimização é importante.
  • \n
  • Compatibilidade com for: No loop for, a variável também é declarada uma vez. Os desenvolvedores queriam manter a uniformidade.
  • \n
  • Simplicidade do compilador: A abordagem antiga era mais simples de implementar no compilador. Mas o preço foi um comportamento não óbvio para os programadores.
  • \n
\n\n

No entanto, a comunidade rapidamente descobriu essa "feature" e começou a relatar bugs. A pergunta no Stack Overflow "Is there a reason for C#'s reuse of the variable in a foreach?" tornou-se uma das mais populares. A Microsoft ouviu e, no C# 5.0 (2012), o comportamento foi alterado.

\n\n

O que mudou no C# 5.0?

\n\n

A partir do C# 5.0, o compilador cria uma nova variável para cada iteração do loop foreach. Agora, o exemplo acima funcionará de forma intuitivamente correta e exibirá 1, 2, 3, 4, 5.

\n\n
// Isto                

Blogs

Book Recommendations