O que é um fechamento ? Nós os temos no .NET?
Se eles existirem no .NET, você poderia fornecer um trecho de código (de preferência em C #) explicando-o?
Eu tenho um artigo sobre esse mesmo tópico . (Ele tem muitos exemplos.)
Em essência, um fechamento é um bloco de código que pode ser executado posteriormente, mas que mantém o ambiente em que foi criado - ou seja, ele ainda pode usar as variáveis locais etc. do método que o criou, mesmo depois disso. O método concluiu a execução.
O recurso geral de fechamentos é implementado em C # por métodos anônimos e expressões lambda.
Aqui está um exemplo usando um método anônimo:
using System;
class Test
{
static void Main()
{
Action action = CreateAction();
action();
action();
}
static Action CreateAction()
{
int counter = 0;
return delegate
{
// Yes, it could be done in one statement;
// but it is clearer like this.
counter++;
Console.WriteLine("counter={0}", counter);
};
}
}
Resultado:
counter=1
counter=2
Aqui podemos ver que a ação retornada por CreateAction ainda tem acesso à variável counter e pode realmente incrementá-la, mesmo que o próprio CreateAction tenha terminado.
counter
está disponível para ser incrementado - o compilador gera uma classe que contém umcounter
campo e qualquer código referente a elecounter
acaba passando por uma instância dessa classe.Se você estiver interessado em ver como o C # implementa o Closure, leia "Conheço a resposta (seu 42) blog"
O compilador gera uma classe em segundo plano para encapsular o método anoymous e a variável j
para a função:
Transformando-o em:
fonte
Encerramentos são valores funcionais que mantêm valores variáveis de seu escopo original. O C # pode usá-los na forma de delegados anônimos.
Para um exemplo muito simples, use este código C #:
No final, bar será definido como 4, e o delegado myClosure poderá ser distribuído para ser usado em outras partes do programa.
Os closures podem ser usados para muitas coisas úteis, como execução atrasada ou para simplificar interfaces - o LINQ é construído principalmente usando closures. A maneira mais imediata de ser útil para a maioria dos desenvolvedores é adicionar manipuladores de eventos a controles criados dinamicamente - você pode usar fechamentos para adicionar comportamento quando o controle é instanciado, em vez de armazenar dados em outro local.
fonte
Um fechamento é uma função anônima passada fora da função em que é criada. Ele mantém quaisquer variáveis da função em que é criado e que usa.
fonte
Aqui está um exemplo artificial para C # que eu criei a partir de código semelhante em JavaScript:
Então, aqui está um código que mostra como usar o código acima ...
Espero que seja um pouco útil.
fonte
Basicamente, o fechamento é um bloco de código que você pode transmitir como argumento para uma função. O C # suporta fechamentos na forma de delegados anônimos.
Aqui está um exemplo simples: o
método List.Find pode aceitar e executar trechos de código (fechamento) para encontrar o item da lista.
Usando a sintaxe C # 3.0, podemos escrever isso como:
fonte
Um fechamento é quando uma função é definida dentro de outra função (ou método) e usa as variáveis do método pai . Esse uso de variáveis localizadas em um método e envolvidas em uma função definida dentro dele é chamado de fechamento.
Mark Seemann tem alguns exemplos interessantes de fechamentos em seu blog, onde ele faz um paralelo entre oop e programação funcional.
E para torná-lo mais detalhado
fonte
Closures são blocos de código que referenciam uma variável fora de si mesmos (abaixo deles na pilha), que podem ser chamados ou executados mais tarde (como quando um evento ou delegado é definido e podem ser chamados em algum momento futuro indefinido) ) ... Como a variável externa à qual o pedaço de código faz referência pode ficar fora de escopo (e de outra forma teria sido perdida), o fato de ser referenciada pelo pedaço de código (chamado de fechamento) diz ao tempo de execução para "manter "essa variável no escopo até que não seja mais necessária pelo pedaço de código de fechamento ...
fonte
Eu tenho tentado entender também, bem abaixo estão os trechos de código para o mesmo código em Javascript e C # mostrando o fechamento.
JavaScript:
C #:
JavaScript:
C #:
fonte
Do nada, uma resposta simples e mais compreensiva do resumo nutricional do livro C # 7.0.
Pré-requisito que você deve saber : Uma expressão lambda pode fazer referência às variáveis e parâmetros locais do método em que está definida (variáveis externas).
Parte real : variáveis externas referenciadas por uma expressão lambda são chamadas de variáveis capturadas. Uma expressão lambda que captura variáveis é chamada de fechamento.
Último ponto a ser observado : As variáveis capturadas são avaliadas quando o delegado é realmente chamado, não quando as variáveis foram capturadas:
fonte
Se você escrever um método anônimo em linha (C # 2) ou (preferencialmente) uma expressão Lambda (C # 3 +), um método real ainda estará sendo criado. Se esse código estiver usando uma variável local de escopo externo - você ainda precisará passar essa variável para o método de alguma forma.
por exemplo, use esta cláusula Linq Where (que é um método de extensão simples que transmite uma expressão lambda):
se você quiser usar i nessa expressão lambda, precisará passá-la para o método criado.
Portanto, a primeira pergunta que se coloca é: deve ser passada por valor ou referência?
Passar por referência é (eu acho) mais preferível quando você obtém acesso de leitura / gravação a essa variável (e é isso que o C # faz; acho que a equipe da Microsoft pesou os prós e contras e seguiu com referência); de acordo com Jon Skeet artigo , Java foi com valor).
Mas então surge outra pergunta: onde alocar esse i?
Deveria realmente / naturalmente ser alocado na pilha? Bem, se você alocá-lo na pilha e passá-lo por referência, pode haver situações em que ela sobrevive ao seu próprio quadro de pilha. Veja este exemplo:
A expressão lambda (na cláusula Where) cria novamente um método que se refere a um i. Se i estiver alocado na pilha do Outlive, quando você enumerar os whereItems, o i usado no método gerado apontará para o i do Outlive, ou seja, para um local na pilha que não está mais acessível.
Ok, então precisamos disso na pilha então.
Então, o que o compilador C # faz para oferecer suporte a esse anônimo / lambda embutido é usar o que é chamado " Closures ": Ele cria uma classe no Heap chamada ( bastante ) DisplayClass que possui um campo que contém i e a Function que realmente usa isto.
Algo que seria equivalente a isso (você pode ver a IL gerada usando ILSpy ou ILDASM):
Instancia essa classe no seu escopo local e substitui qualquer código relacionado a i ou à expressão lambda por essa instância de fechamento. Então - sempre que você estiver usando o i no seu código de "escopo local" onde eu fui definido, você estará realmente usando esse campo de instância DisplayClass.
Portanto, se eu mudasse o "local" i no método principal, ele realmente mudará _DisplayClass.i;
ie
ele imprimirá 12, quando "i = 10" for para esse campo de distribuição e o alterar imediatamente antes da segunda enumeração.
Uma boa fonte sobre o assunto é este módulo Bart De Smet Pluralsight (requer registro) (também ignora seu uso incorreto do termo "Elevação" - o que (eu acho) ele quer dizer é que a variável local (ie i) é alterada para se referir para o novo campo DisplayClass).
Em outras notícias, parece haver alguma concepção errônea de que "Closures" estão relacionados a loops - como eu entendo "Closures" NÃO são um conceito relacionado a loops , mas sim a métodos anônimos / expressões lambda que usam variáveis locais com escopo - embora alguns truques as perguntas usam loops para demonstrá-lo.
fonte
Um fechamento é uma função, definida dentro de uma função, que pode acessar as variáveis locais e seu pai.
então a função dentro do método find.
pode acessar as variáveis dentro de seu escopo, t, e o nome da variável que está no escopo de seus pais. Mesmo que seja executado pelo método find como um delegado, de outro escopo todos juntos.
fonte