¿Qué es un cierre? ? ¿Los tenemos en .NET?
Si existen en .NET, ¿podría proporcionar un fragmento de código (preferiblemente en C #) que lo explique?
Tengo un artículo sobre este mismo tema . (Tiene muchos ejemplos).
En esencia, un cierre es un bloque de código que se puede ejecutar en un momento posterior, pero que mantiene el entorno en el que se creó por primera vez, es decir, todavía puede usar las variables locales, etc. del método que lo creó, incluso después de eso. El método ha terminado de ejecutarse.
La característica general de los cierres se implementa en C # mediante métodos anónimos y expresiones lambda.
Aquí hay un ejemplo usando un 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);
};
}
}
Salida:
counter=1
counter=2
Aquí podemos ver que la acción devuelta por CreateAction todavía tiene acceso a la variable del contador y, de hecho, puede incrementarla, aunque CreateAction haya finalizado.
counter
está disponible para ser incrementado: el compilador genera una clase que contiene uncounter
campo, y cualquier código que haga referenciacounter
termina en una instancia de esa clase.Si está interesado en ver cómo C # implementa Closure, lea "Sé la respuesta (es 42) blog"
El compilador genera una clase en segundo plano para encapsular el método anónimo y la variable j
para la función:
Convirtiéndolo en:
fuente
Los cierres son valores funcionales que mantienen valores variables de su alcance original. C # puede usarlos en forma de delegados anónimos.
Para un ejemplo muy simple, tome este código C #:
Al final, la barra se establecerá en 4, y el delegado de myClosure se puede pasar para usarlo en otra parte del programa.
Los cierres se pueden usar para muchas cosas útiles, como la ejecución retrasada o para simplificar las interfaces: LINQ se construye principalmente con cierres. La forma más inmediata que resulta útil para la mayoría de los desarrolladores es agregar controladores de eventos a los controles creados dinámicamente: puede usar cierres para agregar comportamiento cuando se instancia el control, en lugar de almacenar datos en otro lugar.
fuente
Un cierre es una función anónima pasada fuera de la función en la que se crea. Mantiene cualquier variable de la función en la que se crea que utiliza.
fuente
Aquí hay un ejemplo artificial para C # que creé a partir de un código similar en JavaScript:
Entonces, aquí hay un código que muestra cómo usar el código anterior ...
Espero que sea de alguna ayuda.
fuente
Básicamente, el cierre es un bloque de código que puede pasar como argumento a una función. C # admite cierres en forma de delegados anónimos.
Aquí hay un ejemplo simple: el
método List.Find puede aceptar y ejecutar un fragmento de código (cierre) para encontrar el elemento de la lista.
Usando la sintaxis de C # 3.0 podemos escribir esto como:
fuente
Un cierre es cuando una función se define dentro de otra función (o método) y utiliza las variables del método principal . Este uso de variables que se ubican en un método y se envuelven en una función definida dentro de él, se denomina cierre.
Mark Seemann tiene algunos ejemplos interesantes de cierres en su publicación de blog donde hace un paralelismo entre la programación funcional y oop.
Y para hacerlo más detallado
fuente
Los cierres son fragmentos de código que hacen referencia a una variable fuera de ellos (desde debajo de ellos en la pila), que podrían llamarse o ejecutarse más tarde (como cuando se define un evento o delegado, y podrían llamarse en algún momento futuro indefinido) ) ... Debido a que la variable externa a la que el fragmento de código hace referencia puede estar fuera de alcance (y de lo contrario se habría perdido), el hecho de que se hace referencia al fragmento de código (llamado cierre) le dice al tiempo de ejecución que "retenga "esa variable en alcance hasta que ya no sea necesaria por el fragmento de código de cierre ...
fuente
También he intentado entenderlo, a continuación se encuentran los fragmentos de código para el mismo código en Javascript y C # que muestran el cierre.
JavaScript:
C#:
JavaScript:
C#:
fuente
De la nada, una respuesta simple y más comprensiva del libro C # 7.0.
Requisito previo que debe saber : una expresión lambda puede hacer referencia a las variables y parámetros locales del método en el que se define (variables externas).
Parte real : las variables externas a las que hace referencia una expresión lambda se denominan variables capturadas. Una expresión lambda que captura variables se llama cierre.
Último punto a tener en cuenta : las variables capturadas se evalúan cuando se invoca al delegado, no cuando se capturan las variables:
fuente
Si escribe un método anónimo en línea (C # 2) o (preferiblemente) una expresión Lambda (C # 3 +), todavía se está creando un método real. Si ese código está usando una variable local de alcance externo, de todos modos necesita pasar esa variable al método.
por ejemplo, tome esta cláusula Linq Where (que es un método de extensión simple que pasa una expresión lambda):
si desea usar i en esa expresión lambda, debe pasarlo a ese método creado.
Entonces, la primera pregunta que surge es: ¿se debe pasar por valor o referencia?
Pasar por referencia es (supongo) más preferible a medida que obtienes acceso de lectura / escritura a esa variable (y esto es lo que hace C #; supongo que el equipo de Microsoft sopesó los pros y los contras y fue con referencia; según Jon Skeet's artículo , Java fue con by-value).
Pero luego surge otra pregunta: ¿Dónde asignar ese yo?
¿Debería asignarse realmente / naturalmente en la pila? Bueno, si lo asigna en la pila y lo pasa por referencia, puede haber situaciones en las que sobreviva su propio marco de pila. Toma este ejemplo:
La expresión lambda (en la cláusula Where) nuevamente crea un método que se refiere a una i. Si i está asignado en la pila de Outlive, para cuando enumere whereItems, el i utilizado en el método generado apuntará a i de Outlive, es decir, a un lugar en la pila que ya no es accesible.
Ok, entonces lo necesitamos en el montón entonces.
Entonces, lo que el compilador de C # hace para admitir este anónimo / lambda en línea es usar lo que se llama " Closures ": crea una clase en el montón llamada ( bastante mal DisplayClass ) que tiene un campo que contiene el i, y la función que realmente usa eso.
Algo que sería equivalente a esto (puede ver la IL generada usando ILSpy o ILDASM):
Crea una instancia de esa clase en su ámbito local y reemplaza cualquier código relacionado con yo o la expresión lambda con esa instancia de cierre. Entonces, cada vez que use la i en su código de "ámbito local" donde estaba definida, en realidad está usando ese campo de instancia DisplayClass.
Entonces, si cambiara el "local" i en el método principal, en realidad cambiará _DisplayClass.i;
es decir
imprimirá 12, ya que "i = 10" va a ese campo de clase de muestra y lo cambia justo antes de la segunda enumeración.
Una buena fuente sobre el tema es este módulo de Bart De Smet Pluralsight (requiere registro) (también ignora su uso erróneo del término "elevación" - lo que (creo) que quiere decir es que la variable local (es decir, i) se cambia para referirse al nuevo campo DisplayClass).
En otras noticias, parece haber una idea errónea de que los "cierres" están relacionados con los bucles, ya que entiendo que los "cierres" NO son un concepto relacionado con los bucles , sino más bien con métodos anónimos / expresiones lambda que utilizan variables de ámbito local, aunque algunos trucos las preguntas usan bucles para demostrarlo.
fuente
Un cierre es una función, definida dentro de una función, que puede acceder a las variables locales de la misma, así como a su padre.
entonces la función dentro del método find.
puede acceder a las variables dentro de su alcance, t, y el nombre de la variable que está en su alcance primario. Aunque el método find lo ejecuta como delegado, desde otro ámbito, todos juntos.
fuente