1. Introdução
Relembrando escopo
Imagina que variáveis são funcionários de um escritório gigante, e métodos, loops e blocos de código são salas e escritórios. Alguns funcionários só podem entrar na própria sala, outros podem circular pelo prédio todo. Onde cada um pode ficar — isso é o escopo (scope).
O escopo define onde uma variável declarada é “visível” no programa e onde você pode usar ela.
Principais tipos de escopo
No C# dá pra separar esses tipos principais de escopo:
| Escopo | Exemplo | Onde a variável é "visível" |
|---|---|---|
| Local | Dentro de um método ou bloco | Só dentro desse bloco |
| Parâmetro de método | Na assinatura do método | Só dentro do método |
| Variável de classe (campo) | No corpo da classe fora dos métodos | Em todos os métodos dessa classe |
| Variável dentro de loop/condição | Dentro de do loop/if |
Só dentro desses |
Exemplo com explicações
public class Office
{
int buildingNumber = 50; // Campo da classe: visível em todos os métodos public void PrintInfo() {
int roomNumber = 101; // Variável local: visível só dentro de PrintInfo if (roomNumber > 100) {
int deskNumber = 5; // Visível só dentro desse bloco if Console.WriteLine(deskNumber);
} Console.WriteLine(deskNumber); //
Erro! deskNumber não é mais visível aqui
}
}
2. Funções locais e escopo
Quem enxerga quem?
Quando você declara uma função local dentro de um método (ou até dentro de um loop ou condição), ela fica no mesmo escopo das variáveis declaradas acima. A função local é tipo “parte da mesma sala”.
Exemplo
Função local enxerga variáveis do escopo ao redor
void PrintWithPrefix(string mensagem)
{
string
prefixo = "[LOG]: "; void Print() {
Console.WriteLine(
prefixo +
mensagem); // enxerga as duas variáveis!
} Print();
}
Aqui as variáveis prefixo e mensagem são visíveis dentro da função local Print, porque foram declaradas no mesmo ou em um escopo mais amplo.
Quantos escopos tem aqui?
No exemplo acima:
- tem o escopo do método PrintWithPrefix
- dentro dele — o escopo da função Print
3. Captura de variáveis (Capture)
Captura de variáveis — é quando uma função local usa variáveis que foram declaradas fora dela, mas no mesmo escopo.
Funções locais e métodos anônimos (lambda expressions, a gente vai ver isso já já) guardam (ou “capturam”) todas as variáveis que estavam disponíveis pra elas na hora da declaração.
Dá pra dizer que funções fazem tipo uma foto (capture) do mundo ao redor — e podem usar essas variáveis até quando são chamadas bem depois.
Esquematicamente
Método Main
└─ variável x
└─ função local F() ← "captura" x
Exemplo — captura mais simples
void ExemploContador()
{
int contador = 0;
void Aumentar()
{
contador++; // Essa função captura a variável contador
}
Aumentar();
Aumentar();
Console.WriteLine(contador); // Vai mostrar 2
}
Aqui, depois de duas chamadas da função local Aumentar, o valor de contador vira 2.
4. Usando captura de variáveis
Captura de variáveis deixa fácil “passar” dados entre o escopo do método e funções locais, sem precisar ficar passando um monte de parâmetro.
Se não tivesse captura, você teria que passar todas as variáveis como parâmetro:
void ExemploContadorSemCapture()
{
int contador = 0;
void Aumentar(ref int c)
{
c++;
}
Aumentar(ref contador);
Aumentar(ref contador);
Console.WriteLine(contador);
}
Isso é chato — pra quê ficar escrevendo ref e estragar a assinatura da função, se ela pode “ver” as variáveis de fora numa boa?
5. Funções locais e vida das variáveis depois do método
As variáveis vivem mais que o método?
Se você passa funções locais (ou delegates com lambdas) pra fora do método atual, as variáveis capturadas automaticamente não “morrem” quando o método termina. O CLR (a máquina virtual do .NET) cuida disso — tudo que precisa fica “segurado” na memória.
Exemplo: funções vivem fora do método
Func<int> GetContador()
{
int contagem = 0;
int Incrementar()
{
contagem++;
return contagem;
}
return Incrementar; // Retornando a função pra fora!
}
var contador = GetContador();
Console.WriteLine(contador()); // 1
Console.WriteLine(contador()); // 2
Aqui, mesmo depois do método GetContador acabar, a variável contagem continua viva, porque a função retornada capturou ela. Isso se chama closure — a gente ainda vai ver isso numa aula separada, mas pra funções locais o mecanismo é igualzinho.
6. Erros comuns e cenários interessantes
Sobrescrevendo variável antes de chamar a função local
Às vezes rola de a variável que a função local capturou ser alterada antes dela ser chamada — aí o resultado pode ser diferente do que você esperava.
Exemplo:
void Exemplo()
{
int x = 42;
void PrintX() { Console.WriteLine(x); }
x = 100; // Mudou a variável!
PrintX(); // Vai mostrar 100, não 42!
}
Manha: A função local sempre vê o valor mais recente da variável na hora que ela é chamada.
Captura de variáveis em loop for/foreach (de novo)
Clássico problema: se você tá escrevendo lógica numa entrevista ou num projeto grande, sempre confere: será que eu tô capturando uma variável “viva” do loop, e como ela vai se comportar?
GO TO FULL VERSION