.net closures

¿Qué son ''cierres'' en.NET?



closures (11)

¿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?


EDITAR: revisé el artículo de Jon Skeet para entender qué son los cierres y cómo usarlos en .NET.


Aquí hay un ejemplo inventado para C # que he creado a partir de código similar en JavaScript:

public delegate T Iterator<T>() where T : class; public Iterator<T> CreateIterator<T>(IList<T> x) where T : class { var i = 0; return delegate { return (i < x.Count) ? x[i++] : null; }; }

Entonces, aquí hay un código que muestra cómo usar el código anterior ...

var iterator = CreateIterator(new string[3] { "Foo", "Bar", "Baz"}); // So, although CreateIterator() has been called and returned, the variable // "i" within CreateIterator() will live on because of a closure created // within that method, so that every time the anonymous delegate returned // from it is called (by calling iterator()) it''s value will increment. string currentString; currentString = iterator(); // currentString is now "Foo" currentString = iterator(); // currentString is now "Bar" currentString = iterator(); // currentString is now "Baz" currentString = iterator(); // currentString is now null

Espero que sea de alguna manera útil.


Básicamente, el cierre es un bloque de código que puede pasar como argumento para 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.

// Passing a block of code as a function argument List<int> ints = new List<int> {1, 2, 3}; ints.Find(delegate(int value) { return value == 1; });

Usando la sintaxis de C # 3.0 podemos escribir esto como:

ints.Find(value => value == 1);


De la nada, una respuesta simple y más comprensiva del libro C # 7.0 en pocas palabras.

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 está definido (variables externas).

static void Main() { int factor = 2; //Here factor is the variable that takes part in lambda expression. Func<int, int> multiplier = n => n * factor; Console.WriteLine (multiplier (3)); // 6 }

Parte real : las variables externas a las que hace referencia una expresión lambda se llaman variables capturadas. Una expresión lambda que captura variables se llama cierre.

Último punto que se debe tener en cuenta : las variables capturadas se evalúan cuando el delegado se invoca realmente, no cuando se capturaron las variables:

int factor = 2; Func<int, int> multiplier = n => n * factor; factor = 10; Console.WriteLine (multiplier (3)); // 30


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ía llamarse o ejecutarse más tarde (como cuando se define un evento o delegado y podría llamarse en algún punto futuro indefinido en el tiempo) ) ... Debido a que la variable externa que el fragmento de referencias de código puede haber salido del alcance (y que de otro modo se habría perdido), el hecho de que esté referenciado por el fragmento de código (llamado cierre) le dice al tiempo de ejecución que "mantenga" "esa variable en el alcance hasta que ya no sea necesaria por el fragmento de código de cierre ...


Los cierres son valores funcionales que se mantienen en valores variables desde su alcance original. C # puede usarlos en forma de delegados anónimos.

Para un ejemplo muy simple, tome este código C #:

delegate int testDel(); static void Main(string[] args) { int foo = 4; testDel myClosure = delegate() { return foo; }; int bar = myClosure(); }

Al final, la barra se establecerá en 4, y el delegado de myClosure se puede pasar para usarlo en otro lugar del programa.

Los cierres se pueden utilizar para muchas cosas útiles, como la ejecución retrasada o para simplificar las interfaces: LINQ se crea principalmente mediante cierres. La manera más inmediata que resulta útil para la mayoría de los desarrolladores es agregar manejadores de eventos a controles creados dinámicamente; puede usar cierres para agregar comportamiento cuando se crea una instancia del control, en lugar de almacenar datos en otro lugar.


Si está interesado en ver cómo C # implementa Cierre, lea "Conozco la respuesta (es 42) blog"

El compilador genera una clase en segundo plano para encapsular el método anónimo y la variable j

[CompilerGenerated] private sealed class <>c__DisplayClass2 { public <>c__DisplayClass2(); public void <fillFunc>b__0() { Console.Write("{0} ", this.j); } public int j; }

para la función:

static void fillFunc(int count) { for (int i = 0; i < count; i++) { int j = i; funcArr[i] = delegate() { Console.Write("{0} ", j); }; } }

Convirtiéndolo en:

private static void fillFunc(int count) { for (int i = 0; i < count; i++) { Program.<>c__DisplayClass1 class1 = new Program.<>c__DisplayClass1(); class1.j = i; Program.funcArr[i] = new Func(class1.<fillFunc>b__0); } }


También he intentado entenderlo, muy abajo están los fragmentos de código para el mismo código en Javascript y C # mostrando el cierre.

  1. Recuento No de veces cada evento ha sucedido o ninguna de las veces que se hace clic en cada botón.

JavaScript:

var c = function () { var d = 0; function inner() { d++; alert(d); } return inner; }; var a = c(); var b = c(); <body> <input type=button value=call onClick="a()"/> <input type=button value=call onClick="b()"/> </body>

DO#:

using System.IO; using System; class Program { static void Main() { var a = new a(); var b = new a(); a.call(); a.call(); a.call(); b.call(); b.call(); b.call(); } } public class a { int b = 0; public void call() { b++; Console.WriteLine(b); } }

  1. recuento Total de veces que ha pasado el evento de clic o cuenta total de clics sin consideración de control.

JavaScript:

var c = function () { var d = 0; function inner() { d++; alert(d); } return inner; }; var a = c(); <input type=button value=call onClick="a()"/> <input type=button value=call onClick="a()"/>

DO#:

using System.IO; using System; class Program { static void Main() { var a = new a(); var b = new a(); a.call(); a.call(); a.call(); b.call(); b.call(); b.call(); } } public class a { static int b = 0; public void call() { b++; Console.WriteLine(b); } }


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 más adelante, pero que mantiene el entorno en el que se creó por primera vez, es decir, 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 aún tiene acceso a la variable de contador, y de hecho puede incrementarla, incluso aunque CreateAction haya finalizado.


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 llama cierre.

Mark Seemann tiene algunos ejemplos interesantes de cierres en su publicación de blog donde hace un paralelismo entre oop y programación funcional.

Y para hacerlo más detallado

var workingDirectory = new DirectoryInfo(Environment.CurrentDirectory);//when this variable Func<int, string> read = id => { var path = Path.Combine(workingDirectory.FullName, id + ".txt");//is used inside this function return File.ReadAllText(path); };//the entire process is called a closure.


Un cierre es una función, definida dentro de una función, que puede acceder a las variables locales, así como a su elemento principal.

public string GetByName(string name) { List<things> theThings = new List<things>(); return theThings.Find<things>(t => t.Name == name)[0]; }

por lo que la función dentro del método de búsqueda.

t => t.Name == name

puede acceder a las variables dentro de su alcance, t, y al nombre de la variable que está en el alcance de sus padres. Aunque el método de búsqueda lo ejecuta como un delegado, desde otro ámbito todos juntos.


Func<int, int> GetMultiplier(int a) { return delegate(int b) { return a * b; } } //... var fn2 = GetMultiplier(2); var fn3 = GetMultiplier(3); Console.WriteLine(fn2(2)); //outputs 4 Console.WriteLine(fn2(3)); //outputs 6 Console.WriteLine(fn3(2)); //outputs 6 Console.WriteLine(fn3(3)); //outputs 9

Un cierre es una función anónima que se pasa fuera de la función en la que se creó. Mantiene todas las variables de la función en la que se creó que utiliza.