É tarde demais para adicionar outra resposta?
Eu escrevi uma tonelada de código LINQ para objetos e afirmo que, pelo menos nesse domínio, é bom entender as duas sintaxes para usar o que for mais simples - o que nem sempre é uma sintaxe de pontos.
É claro que há momentos em que a sintaxe de pontos é o caminho a seguir - outros forneceram vários desses casos; no entanto, acho que as compreensões foram modificadas - dado um mau rap, se você quiser. Então, fornecerei uma amostra em que acredito que as compreensões são úteis.
Aqui está uma solução para um quebra-cabeça de substituição de dígitos: (solução escrita usando o LINQPad, mas pode ser autônoma em um aplicativo de console)
// NO
// NO
// NO
//+NO
//===
// OK
var solutions =
from O in Enumerable.Range(1, 8) // 1-9
//.AsQueryable()
from N in Enumerable.Range(1, 8) // 1-9
where O != N
let NO = 10 * N + O
let product = 4 * NO
where product < 100
let K = product % 10
where K != O && K != N && product / 10 == O
select new { N, O, K };
foreach(var i in solutions)
{
Console.WriteLine("N = {0}, O = {1}, K = {2}", i.N, i.O, i.K);
}
//Console.WriteLine("\nsolution expression tree\n" + solutions.Expression);
... que produz:
N = 1, O = 6, K = 4
Não é tão ruim, a lógica flui linearmente e podemos ver que ela apresenta uma única solução correta. Esse quebra-cabeça é fácil de resolver manualmente: raciocinar que 3>> N
0 e O
> 4 * N implica 8> = O
> = 4. Isso significa que há um máximo de 10 casos para testar manualmente (2 por N
5 por 5) O
) Já me desviei o suficiente - esse quebra-cabeça é oferecido para fins de ilustração do LINQ.
Transformações de compilador
O compilador faz muito para traduzir isso em sintaxe de ponto equivalente. Além da segunda efrom
SelectMany
habitual cláusulas subseqüentes serem transformadas em chamadas , temos let
cláusulas que se tornam Select
chamadas com projeções, as quais usam identificadores transparentes . Como estou prestes a mostrar, ter que nomear esses identificadores na sintaxe de pontos afasta a legibilidade dessa abordagem.
Eu tenho um truque para expor o que o compilador faz ao traduzir esse código para sintaxe de ponto. Se você descomentar as duas linhas comentadas acima e executá-las novamente, obterá o seguinte resultado:
N = 1, O = 6, K = 4
árvore de expressão da solução System.Linq.Enumerable + d_ b8.SelectMany (O => Intervalo (1, 8), (O, N) => novo <> f _AnonymousType0 2(O = O, N = N)).Where(<>h__TransparentIdentifier0 => (<>h__TransparentIdentifier0.O != <>h__TransparentIdentifier0.N)).Select(<>h__TransparentIdentifier0 => new <>f__AnonymousType1
2 (<> h_ TransparentIdentifier0 = <> h _TransparentIdentifier0, NO = ((10 * <> h_ TransparentIdentifier0.N) + <> h _TransparentIdentifier0.O))). Selecione (<> h_ TransparentIdentifier1 => novo <> f _AnonymousType2 2(<>h__TransparentIdentifier1 = <>h__TransparentIdentifier1, product = (4 * <>h__TransparentIdentifier1.NO))).Where(<>h__TransparentIdentifier2 => (<>h__TransparentIdentifier2.product < 100)).Select(<>h__TransparentIdentifier2 => new <>f__AnonymousType3
2 (<> h_ TransparentIdentifier2 = <> h _TransparentIdentifier2, K = <> h_ TransparentIdentifier2.product% 10))). Onde (<> h _TransparentIdentifier3 => (((<> h_ TransparentIdentifier3.K! = <> h _TransparentIdentifier3. <> h_ TransparentIdentifier2. <>h _TransparentIdentifier1. <> h_TransparentIdentifier0.O) AndAlso (<> h _TransparentIdentifier3.K! = <> H_ TransparentIdentifier3. <> H _TransparentIdentifier2. <> H_ TransparentIdentifier1. <> H _TransparentIdentifier0.N)) AndAlso ((<> h_ TransparentIdentifier2 . product / 10) == <> h_ TransparentIdentifier3. <> h _TransparentIdentifier2. <> h_ TransparentIdentifier1. <> h_TransparentIdentifier0.O))). Selecione (<> h_ TransparentIdentifier3 => novo <> f _AnonymousType4`3 (N = < > h_ TransparentIdentifier3. <> h _TransparentIdentifier2. <> h_ TransparentIdentifier1. <> h_TransparentIdentifier0.N,O = <> h_Identificador transparente3. <> H_TransparentIdentifier2. <> H_ TransparentIdentifier1. <> H _TransparentIdentifier0.O, K = <> h__TransparentIdentifier3.K))
Colocando cada operador LINQ em uma nova linha, traduzindo os identificadores "indizíveis" para aqueles que podemos "falar", alterando os tipos anônimos para sua forma familiar e alterando o AndAlso
jargão da árvore de expressão para &&
expor as transformações que o compilador faz para chegar a um equivalente na sintaxe de pontos:
var solutions =
Enumerable.Range(1,8) // from O in Enumerable.Range(1,8)
.SelectMany(O => Enumerable.Range(1, 8), (O, N) => new { O = O, N = N }) // from N in Enumerable.Range(1,8)
.Where(temp0 => temp0.O != temp0.N) // where O != N
.Select(temp0 => new { temp0 = temp0, NO = 10 * temp0.N + temp0.O }) // let NO = 10 * N + O
.Select(temp1 => new { temp1 = temp1, product = 4 * temp1.NO }) // let product = 4 * NO
.Where(temp2 => temp2.product < 100) // where product < 100
.Select(temp2 => new { temp2 = temp2, K = temp2.product % 10 }) // let K = product % 10
.Where(temp3 => temp3.K != temp3.temp2.temp1.temp0.O && temp3.K != temp3.temp2.temp1.temp0.N && temp3.temp2.product / 10 == temp3.temp2.temp1.temp0.O)
// where K != O && K != N && product / 10 == O
.Select(temp3 => new { N = temp3.temp2.temp1.temp0.N, O = temp3.temp2.temp1.temp0.O, K = temp3.K });
// select new { N, O, K };
foreach(var i in solutions)
{
Console.WriteLine("N = {0}, O = {1}, K = {2}", i.N, i.O, i.K);
}
Que, se você executar, poderá verificar se o resultado é novamente:
N = 1, O = 6, K = 4
... mas você escreveria um código como este?
Aposto que a resposta é NONBHN (não apenas não, mas não!) - porque é muito complexo. Claro que você pode criar nomes de identificadores mais significativos que "temp0" .. "temp3", mas o ponto é que eles não adicionam nada ao código - eles não fazem o código ter um desempenho melhor, eles não faça com que o código seja lido melhor, eles apenas o tornam feio e, se você o estivesse fazendo manualmente, sem dúvida você o estragaria uma ou três vezes antes de acertar. Além disso, jogar "o jogo do nome" já é difícil o suficiente para identificadores significativos, por isso congratulo-me com a pausa do jogo do nome que o compilador me fornece na compreensão de consultas.
Esse exemplo de quebra-cabeça pode não ser do mundo real o suficiente para você levar a sério; no entanto, existem outros cenários em que as compreensões da consulta brilham:
- A complexidade
Join
e GroupJoin
: o escopo das variáveis de intervalo nas join
cláusulas de compreensão de consulta transformam erros que de outra forma poderiam ser compilados na sintaxe de pontos em erros de tempo de compilação na sintaxe de compreensão.
- Sempre que o compilador introduzir um identificador transparente na transformação da compreensão, as compreensões valem a pena. Isso inclui o uso de qualquer um dos seguintes itens: vários
from
cláusulas, join
e join..into
cláusulas e let
cláusulas.
Conheço mais de uma loja de engenharia em minha cidade natal que proibiu a sintaxe de compreensão. Eu acho que é uma pena, já que a sintaxe da compreensão é apenas uma ferramenta e uma ferramenta útil. Eu acho que é como dizer, "Há coisas que você pode fazer com uma chave de fenda e que não pode fazer com um cinzel. Como você pode usar uma chave de fenda como um cinzel, os formões são proibidos a partir de agora por decreto do rei".