Código equivalente a la palabra clave 'let' en llamadas de método de extensión LINQ encadenadas

191

Usando las funciones de comprensión de consultas de los compiladores de C #, puede escribir código como:

var names = new string[] { "Dog", "Cat", "Giraffe", "Monkey", "Tortoise" };
var result =
    from animalName in names
    let nameLength = animalName.Length
    where nameLength > 3
    orderby nameLength
    select animalName; 

En la expresión de consulta anterior, la letpalabra clave permite pasar un valor a las operaciones where y orderby sin duplicar llamadas a animalName.Length.

¿Cuál es el conjunto equivalente de llamadas al método de extensión LINQ que logra lo que hace aquí la palabra clave "let"?

LBushkin
fuente
11
Para su información, la especificación C # 3.0 explica cada regla de traducción de comprensión de consultas con un detalle insoportable.
Eric Lippert el
17
y para aquellos que encuentran que la especificación va muy fuerte, C # en profundidad de Jon Skeet también lo cubre ;-p
Marc Gravell
Las especificaciones del lenguaje C # son documentos de Word descargables cuyo contenido no está indexado por los motores de búsqueda y no se puede vincular ni navegar en línea. Sería de gran ayuda si las especificaciones estuvieran disponibles en línea.
Olivier Jacot-Descombes

Respuestas:

249

Let no tiene su propia operación; se lleva a cuestas de Select. Puede ver esto si usa "reflector" para separar un dll existente.

será algo como:

var result = names
        .Select(animalName => new { nameLength = animalName.Length, animalName})
        .Where(x=>x.nameLength > 3)
        .OrderBy(x=>x.nameLength)
        .Select(x=>x.animalName);
Marc Gravell
fuente
44
Woah, no sabía que podías autoencapsular usando el nuevo operador así.
David Pfeffer
19
También puede usar el pequeño botón "lambda" en el panel de resultados de LinqPad para ver el código generado si comienza con un Queryable. En otras palabras, si cambia su primera línea a var names = new string [] {"Dog", ...} .AsQueryable (); luego ejecute todo en LinqPad, haga clic en el pequeño botón lambda, verá el código generado prácticamente idéntico a la respuesta de Marc.
Reb.Cabin
3
Necesitaba usar el .Dump()método de extensión en LinqPad para ver la lambda resultante.
justanotherdev
88

Hay un buen artículo aquí.

Básicamente letcrea una tupla anónima. Es equivalente a:

var result = names.Select(
  animal => new { animal = animal, nameLength = animal.Length })
.Where(x => x.nameLength > 3)
.OrderBy(y => y.nameLength)
.Select(z => z.animal);
Keltex
fuente
Cito el artículo anteriorit seems prudent to recommend against using the let keyword in cases where you do not need to transform a variable
JB. Con monica.
Lo cito más:This could be considered a micro-optimisation
Monseñor el
7

También hay un método de extensión .Let en System.Interactive, pero su propósito es introducir una expresión lambda para ser evaluada 'en línea' en una expresión fluida. Por ejemplo, considere (en LinqPad, por ejemplo) la siguiente expresión que crea nuevos números aleatorios cada vez que se ejecuta:

var seq = EnumerableEx.Generate(
    new Random(),
    _ => true,
    _ => _,
    x => x.Next());

Para ver que cada vez aparecen nuevas muestras aleatorias, considere lo siguiente

seq.Zip(seq, Tuple.Create).Take(3).Dump();

que produce pares en los que la izquierda y la derecha son diferentes. Para producir pares en los que la izquierda y la derecha son siempre iguales, haga algo como lo siguiente:

seq.Take(3).ToList().Let(xs => xs.Zip(xs, Tuple.Create)).Dump(); 

Si pudiéramos invocar expresiones lambda directamente, podríamos escribir

(xs => xs.Zip(xs, Tuple.Create))(seq.Take(3).ToList()).Dump();

Pero no podemos invocar expresiones lambda como si fueran métodos.

Cabaña Reb.
fuente
1

sobre el Código equivalente a la palabra clave 'let' en las llamadas de método de extensión LINQ encadenadas

el comentario anterior ya no es válido

var x = new List<int> { 2, 3, 4, 5, 6 }.AsQueryable();
(from val in x
let val1 = val
let val2 = val + 1
where val2 > val1
select val
).Dump();

produce

System.Collections.Generic.List`1[System.Int32]
.Select(
  val =>
     new
     {
         val = val,
         val1 = val
     }
)
.Select(
  temp0 =>
     new
     {
         temp0 = temp0,
         val2 = (temp0.val + 1)
     }
)
.Where(temp1 => (temp1.val2 > temp1.temp0.val1))
.Select(temp1 => temp1.temp0.val)

así que múltiples letestán optimizados ahora

Fabio Angela
fuente