2012-05-14 18 views
18

¿Por qué mi parámetro x se comporta de forma errática?Clarificación de alcance Lambda

  1. Ejemplo 1 - No existe en el contexto actual.
  2. Ejemplo 2: No se puede reutilizar x porque está definido en un ámbito "secundario".
  3. Ejemplo 3 - Bien. Esta es la parte en la que estoy confundido. Tal vez un alcance 'infantil' diferente?

Ejemplo 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 esta compilación error de tiempo:

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

que espero.

Ejemplo 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 este error de tiempo de compilación:

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

entiendo el alcance como respondida en esta pregunta, Is there a reason for C#'s reuse of the variable in a foreach?. Sin embargo, esto es algo que nunca he visto antes. Además, las respuestas a esta pregunta, What is the scope of a lambda variable in C#?, son incompletas o incorrectas.

Ejemplo 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); 

No hay errores producidos.


Con la respuesta aceptada, estas publicaciones de blog de Eric Lippert me ayudaron a entender lo que estaba sucediendo. Si alguien todavía se confunde:

declaration space

simple names

+1

[aquí] (http://blogs.msdn.com/b/ericlippert/archive/2009/11/02 /simple-names-are-not-so-simple.aspx) son dos [enlaces] relevantes (http://blogs.msdn.com/b/ericlippert/archive/2009/11/05/simple-names-are- not-so-simple-part-two.aspx) sobre el tema. – Servy

+0

posible duplicado de [ámbito de variable local en el método anónimo de linq (cierre)] (http://stackoverflow.com/questions/10517964/local-variable-scope-in-linq-anonymous-method-closure) – Magnus

+0

posible duplicado de [ ¿Cuál es el alcance de una variable lambda en C#?] (Http://stackoverflow.com/questions/10494074/what-is-the-scope-of-a-lambda-variable-in-c) – nawfal

Respuesta

16

En Example 1, X se define en el ámbito local de la expresión Lamdba y no es visible para la tercera línea

En Example 2, ahora has declarado dos variables llamadas "x" en el mismo ámbito de declaración (la visibilidad es diferente)

Con un método lambda o anónimo, "captura" el alcance en el que se está ejecutando. Si tiene una x local en el mismo ámbito que la definición lambda, entonces "captura" esa x para extraer a lo que la lambda puede acceder, lo que da como resultado dos definiciones de "x". Lo que declaras en la lambda no se captura en la otra dirección, por lo que no es visible fuera de la lambda.

En Example 3, Ahora no está utilizando una variable que es local solamente la lambda fuera de la lambda, y no nombra algo igual en el mismo ámbito de declaración.

+0

Todavía no me gusta t get, how/why example 2 no compila. Si ex-2 no, entonces incluso ex-3 no debería compilar también. Según mi lógica, la segunda declaración de x "int x" no debería tener ninguna preocupación con la primera, ya que está explícitamente definida y disponible solo para la expresión lambda definida en esa línea. Esto es tan nuevo y extraño para mí. – MrClan

+1

@MrClan consulte [este código] (http://pastebin.com/DvDnNaPz) es similar al problema del alcance y puede ayudarlo a comprender. –

+2

@MrClan no compila porque "x => ..." declara una variable xy "int x = 1;" declara otro. El lambda es un cierre que puede traer "variables externas" que se capturan para cuando se ejecuta el lambda. Para que esas variables sean capturadas, deben ser únicas. "x" podría significar la x definida en la lambda, o la x definida en el ámbito externo, entonces, el compilador no sabe cuál usar en el código "x <3" ... –

4

Los ámbitos secundarios (ejemplo 3) pueden usar las mismas variables, pero padres e hijos no pueden volver a declarar las variables.

Usted puede obtener los mismos por:

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

Este sería un fracaso:

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

No es tan complicado como parece ser.

Si se define un parámetro para una expresión lambda, el parámetro sólo es válida dentro de la alcance de la expresión lambda

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

Si tiene una segunda variable con el definido en el mismo alcance que la expresión lambda , obtendrá un error, porque esta segunda variable también es válida en el ámbito de la expresión 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 
} 
} 

En el tercer ejemplo que tienen 2 expresiones Lamda diferentes y por lo tanto dos diferentes ámbitos