2012-05-08 12 views
63

estoy confundido sobre el alcance de la variable lambda, tomar por ejemplo la siguiente¿Cuál es el alcance de una variable lambda en C#?

var query = 
    from customer in clist 
    from order in olist 
    .Where(o => o.CustomerID == customer.CustomerID && o.OrderDate == // line 1 
     olist.Where(o1 => o1.CustomerID == customer.CustomerID)  // line 2 
      .Max(o1 => o1.OrderDate)         // line 3 
    ) 
    select new { 
     customer.CustomerID, 
     customer.Name, 
     customer.Address, 
     order.Product, 
     order.OrderDate 
    }; 

En la línea 1 Tengo declarar una variable lambda 'o' lo que significa que no se puede declarar otra vez en la línea 2 (o al menos el compilador se queja si intento) ¿Pero no se queja de la línea 3 a pesar de que 'o1' ya existe?

¿Cuál es el alcance de una variable lambda?

+10

Tiene la respuesta a continuación, me gustaría compartir una pista simple y práctica: cuando el alcance no está claro al leer el código, y cuando el alcance importa, o evite la ambigüedad visual usando diferentes nombres de variables; o escriba sus lambdas en forma larga, con llaves, haciéndolas parecer más a funciones: la mente y el ojo de la mayoría de los programadores hacen una distinción más clara cuando se ven las llaves tradicionales. –

Respuesta

170

Los paréntesis indican la clave - la variable lambda es capturado en el ámbito de donde ha declarado:

.Where(o => ... olist.Where(o1 => ...).Max(o1 => ...)) 
    // |----------------------------------------------| scope of o 
    //      |---------|     scope of first o1 
    //          |---------| scope of second o1 

Tenga en cuenta que no hay solapamiento de las dos o1 las variables, pero ambos se superponen (o sombra) de la o variable y, por lo tanto, no puede usar el mismo nombre.

+58

+1 para visualización – Cheezmeister

+2

excelente respuesta ... gracias amigo! – mfc

+3

La mejor respuesta que he visto en SO desde hace 3 años. – kizzx2

4

robaba lambda es la sustitución de la función anónima aquí en su código

mismo alcance

Where(o => o.CustomerID == customer.CustomerID && o.OrderDate == //line 1 
     olist.Where(o1 => o1.CustomerID == customer.CustomerID) //line 2  

es el alcance de la función en la que Varialble "o" vivo

aquí en la línea tres esto es nueva scrop de la variable, es decir, nuevo alcance de función

Diferentes Ámbito

.Max(o1 => o1.OrderDate) )  //line 3 

de manera que es el reson en la línea 1 y línea 2 varialbe "O" se define en la línea 1 no se define en la línea 2 por la misma scrop y "o1" definir en la línea 2 se puede definir de nuevo en línea 3 becauase se está en un ámbito de función diferente

14

El alcance de un parámetro lambda es igual a todo el ámbito del cuerpo de la expresión lambda, incluidas las expresiones o ámbitos internos de lambda.

Si ampliamos la sintaxis de sus expresiones lambda y añadir un poco de sangrado de usar que puede llegar a ser más clara (aunque probablemente no hay nada tan claro como yamen's diagrammatic answer!):

.Where(o => { 
    return o.CustomerID == customer.CustomerID 
     && o.OrderDate == olist.Where(
      o1 => o1.CustomerID == customer.CustomerID 
     ) 
     .Max(
      o1 => o1.OrderDate 
     ); 
}) 

Tenga en cuenta que su llamada .Where().Max() se encuentra dentro de una combinación externa .Where(). El o en la lambda externa está encapsulado por la lambda externa dentro de su lambda interno (esto se llama cierre) de modo que ya existe en el alcance de sus lambdas internos, y no puede reutilizarse como parámetro.

Puede reutilizar o1 porque sus dos lambdas internas están completamente separadas entre sí, por lo que no sobrepasa el alcance de ninguna de las dos.

2

C# no es compatible con el sombreado de esa manera.

El motivo o1 funciona de nuevo, es que no sombrea el anterior o1.

5

No puede usar el mismo nombre de variable en dos ámbitos si uno de los ámbitos contiene el otro.

En su pregunta, o se introduce en el ámbito exterior, por lo que no se puede utilizar de nuevo en el segundo Where() o en Max(), porque estos ámbitos están contenidas en la exterior.

Por otro lado, puede usar o1 en ambos ámbitos internos porque uno no contiene el otro, por lo que no hay ambigüedad.

2

Es lo mismo que con cualquier otra variable. El alcance de o es la expresión completa en su primer Where, por lo que no puede usarlo nuevamente en el segundo, que está dentro del primero. Pero el alcance de o1 es solo la expresión en su segundo Where, por lo que puede usarlo en la expresión de su Max, que está fuera del segundo Where. En el código:

// o scope lasts until the first bracket is closed 
Where(o => o.CustomerID == customer.CustomerID && o.OrderDate == 
// o1 scope lasts until the second bracket is closed; the first is not yet closed here 
     olist.Where(o1 => o1.CustomerID == customer.CustomerID) 
// The second bracket is closed, so o1 is already out of scope; o is still in scope 
      .Max(o1 => o1.OrderDate) 
) 
// The first bracket is closed, so o is finally out of scope 
2

Trato imagen como este como por su código ...

.Where(o => o.CustomerID == customer.CustomerID && o.OrderDate == // line 1 
     olist.Where(o1 => o1.CustomerID == customer.CustomerID)  // line 2 
      .Max(o1 => o1.OrderDate)         // line 3 
    ) 

manera bastante cruda de explicar, pero sus soportes de determinar el alcance. Su segundo o1 no está anidado dentro del segundo, donde, de otra forma usted tendría el mismo problema

//outermost where 

((BEGIN-o 

//inner where 

(BEGIN-o1 END-o1) 

//max 

(BEGIN-o1 END-o1) 

END-o)) 
1
var external = 1; 

//This is a scope 
Action scope = new Action(() => 
{ 
    //myVar is not accessible from outside 
    var myVar = 0 + external; 
    Console.WriteLine(myVar); //outputs 1 
}); 

//Call the scope 
scope(); 
//Console.WriteLine(myVar);//Will not compile 

Cuando se compila el código, toda la lógica del vacío declarado en la acción será ()=>{ ... } movido a un método en el tipo con un nombre destrozado.

El tiempo de ejecución llamará a la función recién creada cuando llegue a ese lugar en la pila.

Puede pasar valores a un alcance/lambda de varias maneras, que es lo mismo para sacarlos.

Las variables declaradas en una lambda no son accesibles fuera con su nombre declarado.

También es posible utilizar el reflejo para extraer el nombre destrozado, pero no estoy seguro de que lo necesite. (Por favor, hágamelo saber si estoy equivocado.)

+0

Mi respuesta es más simple de leer y más general con el propósito de responder la pregunta. Los otros no muestran el alcance o que no puede repetir los nombres en un ámbito, por ejemplo, (o => o.Somethingings.Where (o => o.IsTrue)) // Error porque o es ya en uso. Merezco más representante. – Jay

Cuestiones relacionadas