Código equivalente à palavra-chave 'let' nas chamadas do método de extensão LINQ em cadeia

191

Usando os recursos de compreensão de consulta dos compiladores C #, você pode escrever 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; 

Na expressão de consulta acima, a letpalavra - chave permite que um valor seja passado para as operações where e orderby sem chamadas duplicadas para animalName.Length.

Qual é o conjunto equivalente de chamadas do método de extensão LINQ que alcança o que a palavra-chave "let" faz aqui?

LBushkin
fonte
11
Para sua informação, a especificação C # 3.0 explica todas as regras de conversão de compreensão de consulta com detalhes excruciantes.
11119 Eric Lippert
17
e para aqueles que acham a pesada especificação indo, de Jon Skeet C #, em tampas de profundidade também ;-p
Marc Gravell
As especificações de idioma do C # são documentos do Word para download cujo conteúdo não é indexado pelos mecanismos de pesquisa e não pode ser vinculado nem navegado online. Seria uma grande ajuda se as especificações estivessem disponíveis online.
Olivier Jacot-Descombes

Respostas:

249

Let não tem operação própria; ele pega carona Select. Você pode ver isso se você usar o "refletor" para separar uma 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
fonte
4
Uau, eu não sabia que você poderia se autoencapsular usando o novo operador assim.
David Pfeffer
19
Você também pode usar o pequeno botão "lambda" no painel de resultados do LinqPad para ver o código gerado se você iniciar com um Queryable. Em outras palavras, se você alterar sua primeira linha para var names = new string [] {"Dog", ...} .AsQueryable (); depois execute a coisa toda no LinqPad, clique no pequeno botão lambda, você verá um código gerado praticamente idêntico à resposta de Marc.
Reb.Cabin
3
Eu precisava usar o .Dump()método de extensão no LinqPad para ver o lambda resultante.
justanotherdev
88

Há um bom artigo aqui

letCria essencialmente uma tupla anônima. É 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
fonte
Cito o artigo acimait seems prudent to recommend against using the let keyword in cases where you do not need to transform a variable
JB. Com a Monica.
Cito ainda mais:This could be considered a micro-optimisation
Mons.
7

Há também um método de extensão .Let no System.Interactive, mas seu objetivo é introduzir uma expressão lambda para ser avaliada 'in-line' em uma expressão fluente. Por exemplo, considere (no LinqPad, por exemplo) a seguinte expressão que cria novos números aleatórios toda vez que é executado:

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

Para ver que novas amostras aleatórias aparecem sempre, considere o seguinte

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

que produz pares nos quais esquerda e direita são diferentes. Para produzir pares nos quais esquerda e direita são sempre iguais, faça algo como o seguinte:

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

Se pudéssemos invocar expressões lambda diretamente, poderíamos escrever

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

Mas não podemos invocar expressões lambda como se fossem métodos.

Reb.Cabin
fonte
1

sobre Código equivalente à palavra-chave 'let' nas chamadas do método de extensão LINQ em cadeia

o comentário acima não é mais 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();

produz

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)

tão múltiplos letsão otimizados agora

Fabio Angela
fonte