¿Es demasiado tarde para agregar otra respuesta?
He escrito un montón de código LINQ-to-objects y afirmo que, al menos en ese dominio, es bueno comprender ambas sintaxis para usar el código que sea más simple, que no siempre es sintaxis de puntos.
Por supuesto, hay momentos en que la sintaxis de puntos ES el camino a seguir; otros han proporcionado varios de estos casos; sin embargo, creo que las comprensiones se han modificado brevemente, dado un mal golpe, por así decirlo. Así que proporcionaré una muestra donde creo que las comprensiones son útiles.
Aquí hay una solución para un rompecabezas de sustitución de dígitos: (solución escrita usando LINQPad, pero puede ser independiente en una aplicación de consola)
// 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 produce:
N = 1, O = 6, K = 4
No está mal, la lógica fluye linealmente y podemos ver que surge una única solución correcta. Este rompecabezas es bastante fácil de resolver a mano: razonando que 3>> N
0 y O
> 4 * N implica 8> = O
> = 4. Eso significa que hay un máximo de 10 casos para probar a mano (2 para N
-por- 5 para O
) Me he desviado lo suficiente: este rompecabezas se ofrece con fines ilustrativos de LINQ.
Transformaciones del compilador
El compilador hace mucho para traducir esto en sintaxis de puntos equivalente. Además de las segundas from
cláusulasSelectMany
habituales y posteriores que se convierten en llamadas , tenemos let
cláusulas que se convierten en Select
llamadas con proyecciones, las cuales usan identificadores transparentes . Como estoy a punto de mostrar, tener que nombrar estos identificadores en la sintaxis de puntos elimina la legibilidad de ese enfoque.
Tengo un truco para exponer lo que hace el compilador al traducir este código a la sintaxis de puntos. Si descomenta las dos líneas comentadas arriba y lo ejecuta nuevamente, obtendrá el siguiente resultado:
N = 1, O = 6, K = 4
árbol de expresión de solución System.Linq.Enumerable + d_ b8.SelectMany (O => Range (1, 8), (O, N) => new <> 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))). Seleccione (<> h_ TransparentIdentifier1 => new <> 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))). Donde (<> 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_ TransparentIdentifier3. <> H_ product / 10) == <> h_ TransparentIdentifier3. <> h _TransparentIdentifier2. <> h_ TransparentIdentifier1. <> h _TransparentIdentifier0.O))). Seleccione (<> h_ TransparentIdentifier3 => new <> 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 a cada operador LINQ en una nueva línea, traduciendo los identificadores "indescriptibles" a los que podemos "hablar", cambiando los tipos anónimos a su forma familiar y cambiando la AndAlso
jerga del árbol de expresiones para &&
exponer las transformaciones que hace el compilador para llegar a un equivalente en sintaxis de puntos:
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);
}
Lo que si ejecuta puede verificar que salga nuevamente:
N = 1, O = 6, K = 4
... pero ¿escribirías código como este?
Apuesto a que la respuesta es NONBHN (¡No solo no, sino que no!), Porque es demasiado complejo. Claro que puede encontrar algunos nombres de identificadores más significativos que "temp0" .. "temp3", pero el punto es que no agregan nada al código, no hacen que el código funcione mejor, no lo hacen haga que el código se lea mejor, solo lo desagradable, y si lo hiciera a mano, sin duda lo estropearía una o tres veces antes de hacerlo bien. Además, jugar "el juego de los nombres" es lo suficientemente difícil como para identificar identificadores significativos, por lo que agradezco el descanso del juego de nombres que el compilador me proporciona en la comprensión de consultas.
Es posible que esta muestra de rompecabezas no sea lo suficientemente real como para que la tome en serio; sin embargo, existen otros escenarios donde brillan las comprensiones de consultas:
- La complejidad de
Join
y GroupJoin
: el alcance de las variables de rango en las join
cláusulas de comprensión de consultas convierten los errores que de otro modo podrían compilarse en sintaxis de puntos en errores de tiempo de compilación en la sintaxis de comprensión.
- Cada vez que el compilador introduce un identificador transparente en la transformación de comprensión, las comprensiones valen la pena. Esto incluye el uso de cualquiera de los siguientes: múltiples
from
cláusulas, join
y join..into
cláusulas y let
cláusulas.
Sé de más de un taller de ingeniería en mi ciudad natal que ha prohibido la sintaxis de comprensión. Creo que es una pena, ya que la sintaxis de comprensión no es más que una herramienta y una herramienta útil. Creo que es muy parecido a decir: "Hay cosas que puedes hacer con un destornillador que no puedes hacer con un cincel. Debido a que puedes usar un destornillador como cincel, los cinceles están prohibidos en adelante por decreto del rey".