2012-05-14 12 views
18

Perché il mio parametro x si comporta in modo errato?Chiarimento Lambda

  1. Esempio 1: non esiste nel contesto corrente.
  2. Esempio 2: impossibile riutilizzare x perché è definito in un ambito "figlio".
  3. Esempio 3 - Fine. Questa è la parte in cui sono confuso. Forse una portata "infantile" diversa?

Esempio 1:

List<int> list = new List<int> { 1, 2, 3, 4, 5 }; 
var result = list.Where(x => x < 3); 
Console.Write(result.ElementAt(x)); 

crea questo errore di compilazione:

The name 'x' does not exist in the current context

che mi aspetto.

Esempio 2:

List<int> list = new List<int> { 1, 2, 3, 4, 5 }; 
var result = list.Where(x => x < 3); 
int x = 1; 
Console.Write(result.ElementAt(x)); 

produce questo errore di compilazione:

A local variable named 'x' cannot be declared in this scope because it would give a different meaning to 'x', which is already used in a 'child' scope to denote something else

Capisco la scoping come risposta a questa domanda, Is there a reason for C#'s reuse of the variable in a foreach?. Tuttavia, questo è qualcosa che non ho mai visto prima. Inoltre, rende le risposte a questa domanda, What is the scope of a lambda variable in C#?, incomplete o errate.

Esempio 3:

List<int> list = new List<int> { 1, 2, 3, 4, 5 }; 
List<string> stringList = new List<string> { "A", "B" }; 
var result = list.Where(x => x < 3); 
var result2 = stringList.Where(x => x != "A"); 

Console.Write(result2); 

Nessun errore prodotte.


Con la risposta accettata, questi post di blog di Eric Lippert mi hanno aiutato a comprendere ciò che stava accadendo. Se qualcuno è ancora confuso:

declaration space

simple names

+1

[qui] (http://blogs.msdn.com/b/ericlippert/archive/2009/11/02 /simple-names-are-not-so-simple.aspx) sono due link [rilevanti] (http://blogs.msdn.com/b/ericlippert/archive/2009/11/05/simple-names-are- not-so-simple-part-two.aspx) sull'argomento. – Servy

+0

possibile duplicato di [ambito variabile locale nel metodo linq anonimo (chiusura)] (http://stackoverflow.com/questions/10517964/local-variable-scope-in-linq-anonymous-method-closure) – Magnus

+0

possibile duplicato di [ Qual è lo scopo di una variabile lambda in C#?] (Http://stackoverflow.com/questions/10494074/what-is-the-scope-of-a-lambda-variable-in-c) – nawfal

risposta

16

In Example 1, x è definita nell'ambito locale dell'espressione Lamdba e non è visibile alla terza linea

In Example 2, ora hai dichiarato due variabili denominate "x" allo stesso ambito di dichiarazione (la visibilità è diversa)

Con un metodo lambda o anonimo, "cattura" l'ambito in cui è in esecuzione. Se si ha una x locale nello stesso scope della definizione lambda, allora "cattura" quella x per estrarre in che cosa può accedere la lambda, ottenendo così due definizioni di "x". Ciò che dichiari nella lambda non viene catturato nell'altra direzione, quindi non è visibile al di fuori della lambda.

In Example 3, Ora non si sta utilizzando una variabile che è localizzata solo lambda al di fuori della lambda e che non assegna un nome uguale allo stesso ambito di dichiarazione.

+0

Non lo faccio ancora t ottenere, come/perché l'esempio 2 non viene compilato. Se ex 2 non sarà nemmeno ex 3, non dovrebbe essere compilato. Secondo la mia logica, la seconda dichiarazione di x "int x" non dovrebbe riguardare il primo, poiché è esplicitamente definita e disponibile solo per l'espressione lambda definita su quella linea. Questo è così dannatamente nuovo e strano per me. – MrClan

+1

@MrClan guarda [questo codice] (http://pastebin.com/DvDnNaPz) è simile al problema dell'ambito e può aiutarti a capire. –

+2

@MrClan non viene compilato perché "x => ..." dichiara una variabile x e "int x = 1;" dichiara un altro. Il lambda è una chiusura che può introdurre "variabili esterne" che vengono catturate quando viene eseguito il lambda. Affinché queste variabili possano essere catturate, devono essere uniche. "x" potrebbe significare la x definita nel lambda, o la x definita nello scope esterno - quindi, il compilatore non sa quale usare nel codice "x <3" ... –

4

Gli ambiti figlio (esempio 3) possono utilizzare le stesse variabili, ma padre e figlio non possono ripetere le variabili.

si può ottenere lo stesso con di:

// Child scopes 
for (int i = 1; i < 10; i++){ /* do something */ } 
for (int i = 1; i < 10; i++){ /* do something else */ } 

Questo fallirebbe:

// Child and parent 
for (int i = 1; i < 10; i++){ /* do something */ } 
int i = 33; 
2

Non è così complicato come sembra essere.

Se si definisce un parametro per un'espressione lambda, il parametro è valido solo all'interno della portata dell'espressione Lamda

(int x) => 
{ 
    //x is only valid inside this scope 
} 

Se si dispone una seconda variabile con la definita nello stesso ambito come espressione Lamda , riceverai un errore, perché questa seconda variabile è valida anche nell'ambito di espressione lamda.

void Foo() 
{ 

int y; 

//y is valis in the scope of foo 

(int x) => 
{ 
    //x is only valid inside this scope 
    //y is valid in the scope of foo and the lamda expression 
} 
} 

Nel terzo esempio si hanno 2 differenti espressioni Lamda e quindi due diversi ambiti di