2012-03-06 15 views
45

Le chiusure di espressioni lambda (e fino ad un grado, funzioni anonime)?Le espressioni Lambda sono presenti nelle chiusure C#?

La mia comprensione delle chiusure è che si tratta di funzioni trattate come oggetti, che sembra essere una rappresentazione accurata delle funzioni anonime e delle espressioni Lambda.

Ed è corretto chiamarli chiusure? Capisco che le chiusure sono venute fuori (o sono diventate popolari) a causa del dialetto lisp, ma è anche un termine di programmazione generale?

Grazie per qualsiasi chiarimento che è possibile fornire!

+5

NB sono chiamate chiusure. "Clojure" è una lingua (da qui la mia modifica). –

+0

Ah - questo chiarisce definitivamente questa confusione! Grazie –

+0

Le chiusure sono un aspetto delle espressioni lambda. Lambdas non deve necessariamente supportare chiusure. Alcune lingue lo implementano in modo diverso. Ad esempio, Java è diverso da C# in quanto non consente la modifica della variabile chiusa sopra all'interno della funzione. Detto questo, penso che questa domanda riguardi la comprensione generale, quindi il duplicato di [Qual è la differenza tra una "chiusura" e una "lambda"?] (Http://stackoverflow.com/questions/220658/what-is-the- differenza tra una chiusura e una lambda) – nawfal

risposta

91

Un lambda può essere implementato utilizzando una chiusura, ma non è necessariamente una chiusura.

A closure è "una funzione insieme a un ambiente di riferimento per le variabili non locali di tale funzione.".

Quando si esegue un'espressione lambda che utilizza variabili definite al di fuori del metodo, il lambda deve essere implementato utilizzando una chiusura. Ad esempio:

int i = 42; 

Action lambda =() => { Console.WriteLine(i); }; 

In questo caso, il metodo compilatore generato deve avere accesso alla variabile (i) definita in un ambito completamente diverso. Perché funzioni, il metodo che genera è una "funzione insieme all'ambiente di riferimento" - in pratica, sta creando una "chiusura" per recuperare l'accesso alla variabile.

Tuttavia, questo lambda:

Action lambda2 =() => { Console.WriteLine("Foo"); } 

non si basa su alcun "ambiente riferimento", in quanto si tratta di un metodo completamente contenuto. In questo caso, il compilatore genera un normale metodo statico e non vi è alcuna chiusura implicata.

In entrambi i casi, lambda sta creando un delegate ("oggetto funzione"), ma è solo creando una chiusura nel primo caso, poiché il lambda non ha necessariamente bisogno di "catturare" l'ambiente di riferimento in tutti i casi .

+2

'var lambda2 =() => {Console.WriteLine (" Foo "); } '* Non si basa su alcun" ambiente di riferimento ", poiché è un metodo completamente contenuto. * Si chiude sul sistema IO! :) –

+0

@DanielEarwicker Non considererei un riferimento .NET come chiusura del sistema. La configurazione dell'ambiente operativo in cui opera la funzione, non la creazione di un ambiente specifico per la funzione stessa (che viene passata con esso). C'è una differenza. –

+1

Era solo un commento semiseria: sì, non è necessario che il compilatore C# generi una classe speciale per rappresentare una chiusura.Ma in termini generali una chiusura è una situazione in cui una funzione dipende da un valore che non viene passato in un parametro formale. In questo senso, anche un semplice programma C, con una funzione che si riferisce a una variabile globale, è un esempio di chiusura. –

7

Sì. Le chiusure tipicamente catturano le variabili dall'ambito esterno. Lambdas può farlo. Tuttavia se il tuo lambda non cattura nulla, non è una chiusura.

+0

Con questa definizione un lambda che non accetta variabili esterne dovrebbe essere una chiusura? – Yuck

+2

Lambdas * può * farlo. La risposta di Jason è più accurata. –

+0

Il termine "chiusura" potrebbe essere ben definito, ma nella pratica viene utilizzato in modo approssimativo. Non penso che si possa affermare con certezza che i lambda che non catturano nulla non sono chiusure. – usr

11

È "chiusura" non "clojure".

Questo non è ciò che una chiusura è. Una chiusura è fondamentalmente una rappresentazione di una funzione insieme a qualsiasi variabile non locale che la funzione consuma.

In questo senso, i lambda non sono chiusure, ma causano la generazione di chiusure da parte del compilatore se chiudono sopra qualsiasi variabile.

Se si utilizza ILDASM su un assembly che contiene un lambda che si chiude su alcune variabili, si vedrà in quell'assieme una classe generata dal compilatore che repsresente la funzione e quelle variabili che sono state chiuse. Quello è la chiusura.

Quando si dice

funzioni che vengono trattati come oggetti,

che è normalmente solo "oggetto funzione" (in C# diremmo "delegato") ed è comune nella programmazione funzionale .

59

La risposta di Reed è corretta; Vorrei solo aggiungere alcuni dettagli aggiuntivi:

  • Espressioni lambda e metodi anonimi entrambi hanno la semantica di chiusura; cioè, "catturano" le loro variabili esterne e prolungano la vita di quelle variabili.

  • funzione anonima è il termine che usiamo quando si intende un'espressione lambda o un metodo anonimo. Sì, questo è confuso. Scusate. Era il meglio che riuscivamo a trovare.

  • una funzione che può essere trattata come un oggetto è solo un delegato. Ciò che rende una lambda a è una chiusura che cattura le sue variabili esterne.

  • Le espressioni lambda convertite in alberi di espressione hanno anche semantica di chiusura, abbastanza interessante. E l'implementazione corretta era un dolore al collo, ti dico!

  • "questo" è considerato una "variabile esterna" allo scopo di creare una chiusura anche se "questo" non è una variabile.

+1

Mentre 'this' isn ' In realtà una variabile, non penso sia troppo sorprendente che sia trattata come una sola ai fini della semantica della chiusura. Si comporta in modo molto simile a come si comporterebbe una "variabile" di sola lettura. Inoltre, penso che sia intuitivamente parte del contesto che una chiusura è destinata a catturare. Infine, è un'istanza in cui la falsa (sebbene comune, sembra) ipotesi che le chiusure catturino i valori non sia troppo dannosa: "questo non cambierà sicuramente dopo il fatto! – dlev

+5

@dlev: non è sorprendente per "questo" in un tipo di riferimento. In un tipo di valore, a volte le persone approfittano del fatto che "questo" è logicamente * un alias della variabile che contiene il valore della struct *; chiudere su "questo" quando in un metodo di un tipo di valore mutabile può produrre risultati imprevisti. –

Problemi correlati