Est-il trop tard pour ajouter une autre réponse?
J'ai écrit une tonne de code LINQ-to-objects et je soutiens qu'au moins dans ce domaine, il est bon de comprendre les deux syntaxes afin d'utiliser ce qui rend le code plus simple - ce qui n'est pas toujours la syntaxe à points.
Bien sûr, il y a des moments où la syntaxe à points EST la voie à suivre - d'autres ont fourni plusieurs de ces cas; cependant, je pense que les compréhensions ont été brièvement modifiées - étant donné un mauvais rap, si vous voulez. Je vais donc fournir un exemple où je pense que les compréhensions sont utiles.
Voici une solution à un casse-tête de substitution de chiffres: (solution écrite à l'aide de LINQPad, mais peut être autonome dans une application 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);
... qui produit:
N = 1, O = 6, K = 4
Pas trop mal, la logique s'écoule linéairement et nous pouvons voir qu'elle propose une seule solution correcte. Ce casse-tête est assez facile à résoudre à la main: raisonner que 3>> N
0 et O
> 4 * N implique 8> = O
> = 4. Cela signifie qu'il y a un maximum de 10 cas à tester à la main (2 pour N
-par- 5 pour O
). J'ai assez erré - ce puzzle est proposé à des fins d'illustration LINQ.
Transformations du compilateur
Le compilateur fait beaucoup pour traduire cela en syntaxe point équivalente. Outre la seconde from
clauseSelectMany
habituelle et les clauses suivantes qui sont transformées en appels, nous avons des let
clauses qui deviennent des Select
appels avec des projections, qui utilisent tous deux des identificateurs transparents . Comme je vais le montrer, le fait de devoir nommer ces identificateurs dans la syntaxe à points enlève à la lisibilité de cette approche.
J'ai une astuce pour exposer ce que le compilateur fait en traduisant ce code en syntaxe à points. Si vous décommentez les deux lignes commentées ci-dessus et l'exécutez à nouveau, vous obtiendrez la sortie suivante:
N = 1, O = 6, K = 4
arborescence d'expression de solution 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))). Sélectionnez (<> 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))). Où (<> 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. < Indentifier2. produit / 10) == <> h_ TransparentIdentifier3. <> h _TransparentIdentifier2. <> h_ TransparentIdentifier1. <> h _TransparentIdentifier0.O)))). Sélectionnez (<> h_ TransparentIdentifier3 => nouveau <> 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))
Mettre chaque opérateur LINQ sur une nouvelle ligne, traduire les identificateurs "indescriptibles" en ceux que nous pouvons "parler", changer les types anonymes en leur forme familière et changer le AndAlso
jargon de l'arborescence d'expressions pour &&
exposer les transformations que fait le compilateur pour arriver à un équivalent en syntaxe à points:
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);
}
Si vous exécutez, vous pouvez vérifier qu'il renvoie à nouveau:
N = 1, O = 6, K = 4
... mais écririez-vous jamais du code comme celui-ci?
Je parierais que la réponse est NONBHN (non seulement non, mais enfer non!) - parce que c'est tout simplement trop complexe. Bien sûr, vous pouvez trouver des noms d'identificateurs plus significatifs que "temp0" .. "temp3", mais le fait est qu'ils n'ajoutent rien au code - ils n'améliorent pas le code, ils ne le font pas mieux rendre le code plus lisible, ils ne font que vider le code, et si vous le faisiez à la main, vous le gâcheriez sans doute une ou trois fois avant de le faire correctement. De plus, jouer au "jeu de noms" est assez difficile pour des identifiants significatifs, donc je me réjouis de la rupture avec le jeu de noms que le compilateur me fournit dans les compréhensions de requêtes.
Cet exemple de puzzle peut ne pas être assez réel pour que vous le preniez au sérieux; cependant, d'autres scénarios existent où les compréhensions de requête brillent:
- La complexité de
Join
et GroupJoin
: l'étendue des variables de plage dans les join
clauses de compréhension des requêtes transforme les erreurs qui pourraient autrement se compiler en syntaxe à points en erreurs au moment de la compilation dans la syntaxe de compréhension.
- Chaque fois que le compilateur introduirait un identificateur transparent dans la transformation de compréhension, les compréhensions en valent la peine. Cela comprend l'utilisation de l'un des éléments suivants: plusieurs
from
clauses, join
& join..into
clauses et let
clauses.
Je connais plus d'un atelier d'ingénierie dans ma ville natale qui a interdit la syntaxe de compréhension. Je pense que c'est dommage car la syntaxe de compréhension n'est qu'un outil et un outil utile à cela. Je pense que c'est un peu comme dire: "Il y a des choses que vous pouvez faire avec un tournevis que vous ne pouvez pas faire avec un burin. Parce que vous pouvez utiliser un tournevis comme burin, les burins sont désormais interdits par décret du roi."