2009-04-17 21 views
12

He estado reading this article about closures en la que dicen:¿Qué tiene de especial los cierres?

  • "todas las cañerías es automática"
  • el compilador "crea una clase contenedora" y "extiende la vida de las variables"
  • "se pueden usar las variables locales sin preocupación"
  • el compilador .NET se encarga de la instalación de cañerías para ti, etc.

Así que hice un ejemplo basado en su código y para mí, parece como aunque los cierres simplemente actúan de forma similar a los métodos de nombres regulares que también "se ocupan de las variables locales sin preocupaciones" y en las que "todas las tuberías son automáticas".

¿O qué problema resolvió este "ajuste de variables locales" que hace que los cierres sean tan especiales/interesantes/útiles?

using System; 
namespace TestingLambda2872 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Func<int, int> AddToIt = AddToItClosure(); 

      Console.WriteLine("the result is {0}", AddToIt(3)); //returns 30 
      Console.ReadLine(); 
     } 

     public static Func<int, int> AddToItClosure() 
     { 
      int a = 27; 
      Func<int, int> func = s => s + a; 
      return func; 
     } 
    } 
} 

respuesta

Así que la respuesta a esta pregunta es leer Jon Skeet's article on closures que Marc señaló. Este artículo no solo muestra la evolución que lleva a las expresiones lambda en C#, sino que también muestra cómo se tratan los cierres en Java, una lectura excelente para este tema.

+0

Otros idiomas (como Javascript) admiten cierres. ¿Estás preguntando sobre el concepto o la implementación específica de C#? – strager

+1

Creo que ese ejemplo es demasiado simple para comprender el poder de los cierres. –

+0

@strager: me refiero al concepto en general, estoy tratando de entender los delegados, cierres, mapas, lambdas, "expresiones lambda", "árboles lambda", funciones anónimas, currying, etc. y, en general, el nuevo paradigmas de programación funcional que parecen extenderse más allá de cualquier lengua, tienen sus raíces en las matemáticas, etc. (Puse C# en el título porque Stackoverflow ahora te dice que tu título "no es lo suficientemente bueno, agrega palabras únicas" ...) –

Respuesta

20

Su ejemplo no es claro, y no muestra (IMO) el uso típico de captura (lo único capturado es a, que siempre es 3, por lo que no es muy interesante).

Considere este ejemplo de libro de texto (un predicado):

List<Person> people = ... 
string nameToFind = ... 
Person found = people.Find(person => person.Name == nameToFind); 

Ahora probarlo sin un cierre; que tiene que hacer mucho más trabajo, incluso si somos perezosos:

PersonFinder finder = new PersonFinder(); 
finder.nameToFind = ... 
Person found = people.Find(finder.IsMatch); 
... 
class PersonFinder { 
    public string nameToFind; // a public field to mirror the C# capture 
    public bool IsMatch(Person person) { 
     return person.Name == nameToFind; 
    } 
} 

enfoque La captura se extiende además a una gran cantidad de variables en diferentes ámbitos - un montón de complejidad que se oculta.

Aparte de los nombres, lo anterior es una aproximación de lo que hace el compilador de C# detrás de las escenas. Tenga en cuenta que cuando se usan ámbitos adicionales, comenzamos a encadenar las diferentes clases de captura (es decir, los ámbitos internos tienen una referencia a la clase de captura de ámbitos externos). Bastante complejo.

Jon Skeet tiene un buen article on this here, y más in his book.

+0

(a la pregunta eliminada sobre "static"): No, ya que diferentes subprocesos pueden querer buscar diferentes nombres al mismo tiempo o diferir la ejecución del delegado. –

+1

+1 para C# en la parte de profundidad en cierres –

+2

+1 para esta oración en C# en Artículo de cierres de profundidad: "los cierres le permiten encapsular algún comportamiento, pasarlo como cualquier otro objeto y tener acceso al contexto en que fueron declarados primero " –

0

El cierre es una funcionalidad del compilador. No lo ves, solo hace que el código que escribes funcione.

Sin él, la llamada a AddToIt (3) fallará, porque la lamda subyacente utiliza la variable local a = 27 en el ámbito de AddToItClusure(). Esta variable no existe cuando se llama AddToit.

Pero debido al Cierre, un mecanismo utilizado por el compilador, el código funciona y no tiene por qué preocuparse.

Cuestiones relacionadas