CodeGym /Cursos /C# SELF /Captura de variáveis por funções locais

Captura de variáveis por funções locais

C# SELF
Nível 11, Lição 5
Disponível

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
Funções locais não criam uma “sala” separada de verdade: elas podem ver tudo que tem no corredor (método).

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
}
Função local captura variável do escopo externo

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
Closure: variável contagem vive depois que o método termina

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?

2
Tarefa
C# SELF, nível 11, lição 5
Bloqueado
Uso simples de função local e captura de variáveis
Uso simples de função local e captura de variáveis
2
Tarefa
C# SELF, nível 11, lição 5
Bloqueado
Inicialização de um contador numérico com função local
Inicialização de um contador numérico com função local
1
Pesquisa/teste
Tuplas e funções locais, nível 11, lição 5
Indisponível
Tuplas e funções locais
Vantagens das tuplas sobre out e tipos anônimos
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION