Como contar uma função lambda para capturar uma cópia em vez de uma referência em C #?

StackOverflow https://stackoverflow.com/questions/451779

  •  19-08-2019
  •  | 
  •  

Pergunta

Fui aprendendo C #, e eu estou tentando entender lambdas. Neste exemplo abaixo, ele imprime 10 de dez vezes.

class Program
{
    delegate void Action();
    static void Main(string[] args)
    {
        List<Action> actions = new List<Action>();

        for (int i = 0; i < 10; ++i )
            actions.Add(()=>Console.WriteLine(i));

        foreach (Action a in actions)
            a();
    }
}

Obviamente, a classe gerada por trás do lambda está armazenando uma referência ou um ponteiro para a variável int i, e é a atribuição de um novo valor para a mesma referência de cada vez que o itera ansa. Existe uma maneira de forçar o lambda para pegar uma cópia em vez disso, como o C ++ 0x sintaxe

[&](){ ... } // Capture by reference

vs.

[=](){ ... } // Capture copies
Foi útil?

Solução

O que o compilador está fazendo está puxando o seu lambda e quaisquer variáveis ??capturado pela lambda em um compilador gerado classe aninhada.

Após a compilação seu exemplo parece muito com isto:

class Program
{
        delegate void Action();
        static void Main(string[] args)
        {
                List<Action> actions = new List<Action>();

                DisplayClass1 displayClass1 = new DisplayClass1();
                for (displayClass1.i = 0; displayClass1.i < 10; ++displayClass1.i )
                        actions.Add(new Action(displayClass1.Lambda));

                foreach (Action a in actions)
                        a();
        }

        class DisplayClass1
        {
                int i;
                void Lambda()
                {
                        Console.WriteLine(i);
                }
        }
}

Ao fazer uma cópia dentro do loop, o compilador gera novos objetos em cada iteração, assim:

for (int i = 0; i < 10; ++i)
{
    DisplayClass1 displayClass1 = new DisplayClass1();
    displayClass1.i = i;
    actions.Add(new Action(displayClass1.Lambda));
}

Outras dicas

A única solução que eu fui capaz de encontrar é fazer uma cópia local em primeiro lugar:

for (int i = 0; i < 10; ++i)
{
    int copy = i;
    actions.Add(() => Console.WriteLine(copy));
}

Mas eu estou tendo dificuldade para entender por que colocar uma cópia dentro do loop for é diferente do que ter o i captura lambda.

A única solução é fazer uma cópia local e referência que dentro do lambda. Todas as variáveis ??em C # (e VB.Net) quando acedida em um fecho terá semântica de referência versus semântica cópia / valor. Não há nenhuma maneira de mudar esse comportamento em qualquer idioma.

Nota: Não realmente compilar como uma referência. Os monta-cargas de compilador a variável em uma classe de fecho e redireccionamentos acessos de "I" em um campo de "i" dentro da classe dada fecho. É muitas vezes mais fácil pensar nisso como semântica de referência embora.

Lembre-se que as expressões lambda são o açúcar realmente só sintático para métodos anônimos.

Dito isto, o que você está procurando realmente é como métodos anônimos usar variáveis ??locais em um escopo pai.

Aqui está um link que descreve este. http://www.codeproject.com/KB/cs/InsideAnonymousMethods.aspx# 4

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top