Чи не пізно додати ще одну відповідь?
Я написав тонну коду LINQ до об’єктів, і я стверджую, що принаймні в цьому домені добре розуміти обидва синтаксиси, щоб використовувати те, що робиться для більш простого коду - що не завжди є кращим синтаксисом.
Звичайно, бувають випадки, коли синтаксис крапок 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.Eumerable + d_ b8.SelectMany (O => Діапазон (1, 8), (O, N) => новий <> 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) + <> ч _TransparentIdentifier0.O))). Виберіть (<> H_ TransparentIdentifier1 => новий <> е _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 = <> ч _TransparentIdentifier2, К = ( <> h_ TransparentIdentifier2.product% 10))). Де (<> h _TransparentIdentifier3 => ((((<<h_ TransparentIdentifier3.K! = <> h _TransparentIdentifier3. <> h_ TransparentIdentifier2. <>h _TransparentIdentifier1. <> h_TransparentIdentifier0.O) AndAlso (<> ч _TransparentIdentifier3.K! = <> H_ TransparentIdentifier3. <> Ч _TransparentIdentifier2. <> H_ TransparentIdentifier1. <> Ч _TransparentIdentifier0.N)) AndAlso ((<> h_ TransparentIdentifier3. <> Ч _TransparentIdentifier2. product / 10) == <> h_ TransparentIdentifier3. <> h _TransparentIdentifier2. <> h_ TransparentIdentifier1. <> h _TransparentIdentifier0.O))) Виберіть (<> h_ TransparentIdentifier3 => новий <> 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
... але ти коли-небудь писав би такий код?
Я б став до відповіді: NONBHN (не тільки ні, але пекло ні!) - тому що це занадто складно. Звичайно, ви можете придумати більш значущі імена ідентифікаторів, ніж "temp0" .. "temp3", але справа в тому, що вони нічого не додають до коду - вони не роблять код ефективнішим, вони не роблять зробіть код читати краще, вони лише потворно знімають код, і якби ви це робили вручну, без сумніву, ви зіпсуєте його час або три, перш ніж правильно це зробити. Крім того, грати в «гру з іменами» досить складно для значущих ідентифікаторів, тому я вітаю перерву від гри з іменами, яку компілятор надає мені в розумінні запитів.
Цей зразок головоломки може бути недостатньо реальним , щоб ви сприйняли його серйозно; однак існують інші сценарії, де світяться розуміння запитів:
- Складність
Join
та GroupJoin
: розміщення змінних діапазону в join
застереженнях про розуміння запитів перетворюють помилки, які в іншому випадку можуть скластись у крапці-синтаксисі, у помилки часу компіляції у синтаксисі розуміння.
- Кожного разу, коли компілятор вводить прозорий ідентифікатор у трансформацію розуміння, розуміння стає доцільним. Сюди входить використання будь-якого з наступного: кілька
from
пропозицій, join
& join..into
пунктів і let
застережень.
Я знаю про більше ніж один інженерний цех у моєму рідному місті, який заборонив синтаксис розуміння. Я думаю, що це шкода, оскільки синтаксис розуміння - це лише інструмент і корисний при цьому. Я думаю, що це дуже схоже на те, що говорити: "Є речі, які можна зробити за допомогою викрутки, яку неможливо зробити із стамескою. Оскільки ви можете використовувати викрутку як зубило, долота забороняються відтепер указом короля".