Не слишком ли поздно добавить еще один ответ?
Я написал тонну кода LINQ-to-objects, и я утверждаю, что по крайней мере в этой области хорошо понимать оба синтаксиса, чтобы использовать тот, который создает более простой код - что не всегда является точечным синтаксисом.
Конечно , бывают случаи , когда дот-синтаксис IS путь - другие представили несколько таких случаев; Тем не менее, я думаю, что понимание было коротким - дал плохой рэп, если хотите. Поэтому я приведу пример, где я считаю, что понимание полезно.
Вот решение головоломки подстановки цифр: (решение написано с использованием LINQPad, но может быть автономно в консольном приложении)
// 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);
... какие выводы:
N = 1, O = 6, K = 4
Не так уж и плохо, логика течет линейно, и мы видим, что она предлагает единственное правильное решение. Эту загадку достаточно легко решить вручную: 3>> N
0 и O
> 4 * N подразумевают 8> = O
> = 4. Это означает, что есть максимум 10 случаев для ручного тестирования (2 для N
-by- 5 для O
). Я заблудился достаточно - эта головоломка предлагается для иллюстрации LINQ.
Преобразования компилятора
Компилятор многое делает, чтобы перевести это в эквивалентный точечный синтаксис. Помимо обычных вторых и последующих from
предложений превращаются в SelectMany
вызовы, у нас есть let
предложения, которые становятся Select
вызовами с проекциями, оба из которых используют прозрачные идентификаторы . Как я собираюсь показать, необходимость называть эти идентификаторы в точечном синтаксисе лишает возможности чтения такого подхода.
У меня есть хитрость для разоблачения того, что делает компилятор при переводе этого кода в точечный синтаксис. Если вы раскомментируете две закомментированные строки выше и запустите его снова, вы получите следующий вывод:
N = 1, O = 6, K = 4
дерево выражений решения 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))). Выберите (<> 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))). Где (<> 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_ TransparentIdentifier . product / 10) == <> h_ TransparentIdentifier3. <> h _TransparentIdentifier2. <> h_ TransparentIdentifier1. <> h _TransparentIdentifier0.O))). Выберите (<> h_ TransparentIdentifier3 => new <> f _AnonymousType4`3 (N = << > h_ TransparentIdentifier3. <> h _TransparentIdentifier2. <> h_ TransparentIdentifier1. <> h _TransparentIdentifier0.N,O = <> h_ TransparentIdentifier3. <> H_TransparentIdentifier2. <> H_ TransparentIdentifier1. <> H _TransparentIdentifier0.O, K = <> h__TransparentIdentifier3.K))
Помещая каждый оператор LINQ в новую строку, переводя «невыразимые» идентификаторы в те, которые мы можем «говорить», изменяя анонимные типы в их привычную форму и изменяя язык AndAlso
выражения дерева выражений, чтобы &&
представить преобразования, которые компилятор делает для получения эквивалентных в точечном синтаксисе:
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);
}
Который, если вы запустите, вы можете проверить, что он снова выводит:
N = 1, O = 6, K = 4
... но ты когда-нибудь написал бы такой код?
Держу пари, что ответ НОНБН (не только нет, но, черт возьми, нет!), Потому что это слишком сложно. Конечно, вы можете придумать более значимые имена идентификаторов, чем «temp0» .. «temp3», но дело в том, что они ничего не добавляют в код - они не делают код лучше, они не делают сделать код лучше читаемым, он только уродлив код, и если вы делали это вручную, без сомнения, вы бы испортили его раз или три, прежде чем сделать это правильно. Кроме того, играть в «игру с именем» достаточно сложно для значимых идентификаторов, поэтому я приветствую перерыв в игре с именами, которую компилятор предоставляет мне в понимании запросов.
Этот образец головоломки может оказаться недостаточно реальным для вас, чтобы воспринимать его всерьез; Тем не менее, существуют другие сценарии, где понимание запросов блестит:
- Сложность
Join
и GroupJoin
: область видимости переменных диапазона в join
предложениях по пониманию запросов превращает ошибки, которые в противном случае могли бы компилироваться в синтаксисе точек, в ошибки времени компиляции в синтаксисе понимания.
- Каждый раз, когда компилятор вводит прозрачный идентификатор в преобразование понимания, понимание становится полезным. Это включает в себя использование любого из следующего: множественные
from
предложения, join
& join..into
предложения и let
предложения.
Я знаю больше, чем один инженерный магазин в моем родном городе, где запрещен синтаксис понимания. Я думаю, что это жаль, поскольку синтаксис понимания - всего лишь инструмент и полезный. Я думаю, что это очень похоже на высказывание: «Есть вещи, которые вы можете сделать с помощью отвертки, которые вы не можете сделать с помощью зубила. Поскольку вы можете использовать отвертку в качестве зубила, отныне зубья запрещены по указу короля».