![]() |
Neste artigo vou apresentar o conceito de Closures e seu uso na linguagem C#. |
![]() |
O que são Closures ? (Clausura em português)
Closures é um conceito geralmente associado com as linguagens de programação funcionais (JavaScript, F#, etc.) que vinculam uma função ao seu ambiente de referência, permitindo o acesso a variáveis fora do escopo da função.
Na linguagem C# podemos usar Closures usando delegates.
A linguagem JavaScript usa muito esse recurso e o conceito de closure a seguir foi obtido no site JavaScript Brasil (que parece não estar mais atendendo).
Um closure é uma função interior que tem acesso a variáveis de uma função exterior – cadeia de escopo.
O closure tem três cadeias de escopo:
A função interior tem acesso não somente as variáveis da função exterior, mas também aos parâmetros dela.
Observe que a função interior não pode chamar o objeto(argumentos) da função exterior, entretanto, pode chamar parâmetros da função externa diretamente.
Abaixo vemos um exemplo de Closure usando JavaScript: (O exemplo abaixo usa o Nodejs)
var contar = (function () { var contador = 0; return function () { return contador += 1; } })(); |
![]() |
Para testar a função invocamos variável contar() 3 vezes e notamos que o contador foi incrementado.
A
variável contar é atribuída ao valor de retorno de uma função que é auto
invocada.
A função auto invocada só é executada uma vez. Ela define o contador
igual a zero(0), e retorna uma expressão de função.
Desta forma a variável contar torna-se uma função. O pulo do gato é
que ela pode acessar o contador no escopo pai.
Isso torna possível para uma função ter variáveis 'privadas'.
O contador é protegido pelo escopo da função anônima, e só pode ser alterado
usando a função contar.
Bem, tudo isso para mostrar o conceito de Closure.
Mas onde entra a linguagem C# nessa história ???
Usando Closures com C#
Na
linguagem C#, closures podem ser criadas usando métodos
anônimos ou expressões lambdas (vai depender da sua versão do .NET
Framework).
Quando você cria uma função, as variáveis que ela vai usar e que estão fora do
escopo de visibilidade, são copiadas e armazenadas com o código da closure.
Assim, elas podem ser usadas sempre que o delegate for chamado.
Isso nos traz uma grande flexibilidade quando usamos delegates mas também
introduz a possibilidade e erros inesperados.
Vamos então iniciar com um exemplo bem simples criado no
VS 2015 Community usando uma aplicação Console Application:
class Program
{
static void Main(string[] args)
{
int naoLocal = 1;
Action closure = delegate
{
Console.WriteLine("{0} + 1 = {1}", naoLocal, naoLocal + 1);
};
closure();
Console.ReadKey();
}
}
|
![]() |
Neste código criamos um variável chamada naoLocal a atribuímos o valor inteiro 1.
A segunda instrução cria uma instância do delegate Action o qual gera uma mensagem que usa o valor da variável naoLocal.
Depois chamamos o delegate closure() para ver a mensagem.
O resultado é visto na figura ao lado.
Podemos fazer a mesma coisa usando expressões lambdas :
static void Main(string[] args)
{
int naoLocal = 1;
Action closure = () =>
{
Console.WriteLine("{0} + 1 = {1}", naoLocal, naoLocal + 1);
};
closure();
Console.ReadKey();
}
|
Nada muito diferente do que você poderia esperar. Não é mesmo ???
Vejamos então outro exemplo um pouco mais elaborado.
class Program
{
static Action closure;
static void Main(string[] args)
{
DefineClosure();
closure();
Console.ReadKey();
}
private static void DefineClosure()
{
int naoLocal = 1;
closure = () =>
{
Console.WriteLine("{0} + 1 = {1}", naoLocal, naoLocal + 1);
};
}
}
|
Neste exemplo closure esta em uma variável Action a nível de classe.
O método Main() chama o método DefineClosure() para inicializar a closure antes de executá-la.
Você pode ver que a variável inteira é criada e inicializada e então usada dentro da closure.
Após a conclusão do método DefineClosure, esta variável inteira sai do escopo.
No entanto, ainda estamos invocando o delegado depois que isso acontecer.
Agora, o resultado não é tão óbvio.
Será que vai compilar e executar corretamente ?
Executando
o código você obterá o mesmo resultado do exemplo anterior.
Isso é o Closure em ação.
A variável naoLocal foi capturada pelo código do delegate, fazendo com
que ela permaneça no escopo além dos limites normais.
Na verdade ela permanecerá disponível até que as outras referências ao delegate
permaneçam.
Funcionou, mas na verdade os Closures não tem um suporte no .NET
Framework.
O que realmente acontece é que nos bastidores ocorre o trabalho do compilador.
Quando você constrói o seu projeto, o compilador gera uma nova classe oculta,
que encapsula as variáveis não locais e o código que você inclui no método ou
expressão lambda anônimo.
O código
está incluído em um método e as variáveis locais são representados como campos.
Este novo método da classe é chamado quando o delegate for executado.
Nota : A linguagem C# captura as próprias variáveis usadas nas closures e não o seu valor.
Até o próximo artigo sobre C#.
"Porque pela graça sois salvos, por meio
da fé; e isto não vem de vós, é dom de Deus.
Não vem das obras, para que ninguém se glorie;"
Efésios 2:8,9
Veja os
Destaques e novidades do SUPER DVD Visual Basic
(sempre atualizado) : clique e confira !
Quer migrar para o VB .NET ?
Quer aprender C# ??
Quer aprender os conceitos da Programação Orientada a objetos ? Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ? |
Referências:
Super DVD Vídeo Aulas - Vídeo Aula sobre VB .NET, ASP .NET e C#
Super DVD C# - Recursos de aprendizagens e vídeo aulas para C#
Curso Fundamentos da Programação Orientada a
Objetos com VB .NET