Aventures avec le mot clé "let" dans LINQ to Objects

This post is also available in english here .

Je me suis finalement décidé à blogger dans les deux langues, français et anglais. BlogEngine.NET ne me permet pas d'écrire mes posts dans les deux langues, et après avoir fait quelques essais de site multilingue sur la même url, il se trouve que Google n'indexe pas vraiment très bien ce genre de contenu dynamique.

Quoi qu'il en soit, j'ai eu le temps dernièrement de jouer avec LINQ to objects, et en particulier avec le mot clé "let".

J'avais un grand nombre de fichiers XML placés dans des répertoires multiples, et je voulais les filtrer avec une expression régulière. D'abord, voici comment j'ai récupéré tous les fichiers dans mes répertoires :

  var files = from dir in Directory.GetDirectories(rootPath, SearchOption.AllDirectories)
              from file in Directory.GetFiles("", "*.*")
              select new { Path = dir, File = Path.GetFileName(file) };

Arrivé à ce point, j'aurais pu éviter d'utiliser Directory.GetDirectories puisque GetFiles permet également de chercher recursivement. Mais puisque GetFiles ne retourne qu'un seul tableau de String et non un énumérateur, cela aurait voulu dire que tous mes fichiers auraient été retournées en un seul tableau, ce qui n'est pas très efficace en terme de consommation mémoire. J'aurais préféré avoir une implémentation de GetDirectories et GetFiles basée sur un itérateur, mais la plus fine énumération ne peut être faite que comme ca, sans appel vers du code natif.

Ensuite, ayant tous mes fichiers, je voulais à présent les filtrer en utilisant une expression régulière, puisque les fichiers que je voulais analyser suivaient un format de nom spécifique. J'ai donc mis à jour ma requête comme suit :

    Regex match = new Regex(@"(?<value>\d{4}).xml");
    var files2 = from dir in Directory.GetDirectories(args[0], "*", SearchOption.AllDirectories)
                 from file in Directory.GetFiles(dir, "*.xml")
                 let r = match.Match(Path.GetFileName(file))
                 where r.Success
                 select new {
                    Path = dir,
                    File = Path.GetFileName(file),
                    Value = r.Groups["value"].Value
                 };

Cette fois, j'ai introduit le mot clé let. Ce mot clé est intéressant puisqu'il permet la création d'une variable locale à la requête qui peut contenir à la fois des collections ou des objets simples. Le contenu de cette variable peut être réutilisé dans la clause where, comme source d'une autre requête from, ou dans le select.

Dans mon cas, je voulais simplement avoir le résultat de l'évaluation de mon expression régulière, donc j'ai simplement appelé Regex.Match pour valider le nom du fichier. Enfin, je place le contenu d'un groupe de l'expression dans le type anonyme résultant.

Avec tous mes fichiers filtrés, j'ai constaté ensuite que certains n'étaient pas valides puisqu'il ne contenaient pas un noeud XML spécifique. J'ai adapté ma requête comme cela :

    var files2 = from dir in Directory.GetDirectories(args[0], "*", SearchOption.AllDirectories)
                 from file in Directory.GetFiles(dir, "*.xml")
                 let r = match.Match(Path.GetFileName(file))
                 let c = XElement.Load(file).XPathSelectElements("
//dummy")
                 where r.Success && c.Count() != 0
                 select new {
                    Path = dir,
                    File = Path.GetFileName(file),
                    Value = r.Groups["value"].Value
                 };

J'ai ajouté un nouveau let pour me permettre de charger le document XML, et m'assurer que le noeud nommé "dummy" existe quelque part dans le codument. Au passage, si vous cherchez XPath dans XLinq, regardez par ici.

Vous vous demandez peut-être quand le XElement.Load est effectivement évalué... et bien il n'est sulement évalué lors de l'appel du c.Count(), qui ne l'est que lorsque la regex a validé le fichier concerné. De cette manière, je ne suis pas en train d'essayer de charger tous les fichiers retournés par GetFiles. Il faut se rappeler que les requêtes LINQ ne sont évaluées que lorsqu'elles sont énumérées.

En conclusion, LINQ est une technologie particulierement intéressante, et définitivement pas seulement réservée pour l'interrogation de bases de données. Ce que j'apprécie le plus et qu'il est possible d'écrire du code pratiquement sans boucles, ce qui permet de réduire les effets de bord.

Si vous n'avez pas jeté un coup d'oeuil sur LINQ, essayez, vous aprécierez probablement !

Publié samedi 10 mai 2008 17:35 par jay
Classé sous , ,
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

Commentaires

# re: Aventures avec le mot clé "let" dans LINQ to Objects @ samedi 10 mai 2008 18:56

Interressant le coup de l'évaluation du mot clé let, je ne connaissais pas.

A noter que tu peux avoir

from

let

where

let

select

Par exemple

var q = from i in Enumerable.Range(0, 10)

       let multiply2 = (i % 2) == 0

       where multiply2

       let multiply3 = (i % 3) == 0

       where multiply3

       select i;

Raptor a fait un joli schéma expliquant la syntaxe d'une requete linq pour ceux qui veulent en savoir d'avantages : http://blogs.codes-sources.com/raptorxp/archive/2007/11/19/c-3-0-syntaxe-d-une-requ-te-linq.aspx

cyril

# re: Aventures avec le mot clé "let" dans LINQ to Objects @ samedi 10 mai 2008 19:54

J'ai regardé avec ton exemple l'IL généré. Le mot clé let ne fais "que" rajouter un select

en gros

from o in l

let b = foo(o) //  foo return Boolean

where b

select o;

sera traduit en

l

.Select( o =&gt; return new {o, b = foo(o)}) // le let est ici

.Where( a =&gt; a.b)

.Select( a  =&gt; a.o);

donc l'instruction let r = ... n'est jamais évalué, en fait ca permet juste de retourner un nouveau IEnumerable avec une propriété en plus.

(PS : je me suis compris ;))

cyril

# re: Aventures avec le mot clé "let" dans LINQ to Objects @ dimanche 11 mai 2008 00:31

Oui en effet, le code généré est plutôt simple, quoique bien qu'il soit plus aisé de le comprendre exprimé avec LINQ :)

jay


Les 10 derniers blogs postés

- Les pièges de l’installation de Visual Studio 2017 par Blog de Jérémy Jeanson le 03-24-2017, 13:05

- UWP or not UWP sur Visual Studio 2015 ? par Blog de Jérémy Jeanson le 03-08-2017, 19:12

- Désinstallation de .net Core RC1 Update 1 ou SDK de Core 1 Preview 2 par Blog de Jérémy Jeanson le 03-07-2017, 19:29

- Office 365: Ajouter un utilisateur ou groupe dans la liste des Site collection Administrator d’un site SharePoint Online via PowerShell et CSOM par Blog Technique de Romelard Fabrice le 02-24-2017, 18:52

- Office 365: Comment créer une document library qui utilise les ContentTypeHub avec PowerShell et CSOM par Blog Technique de Romelard Fabrice le 02-22-2017, 17:06

- [TFS] Supprimer en masse les dépendances à SQL Enterprise ou Developer avant de procéder à une migration par Blog de Jérémy Jeanson le 02-20-2017, 20:30

- Office 365: Attention au volume utilisé par les fichiers de Thèmes de SharePoint Online par Blog Technique de Romelard Fabrice le 02-07-2017, 18:19

- [SCVMM] Supprimer une machine bloquée par Blog de Jérémy Jeanson le 01-31-2017, 21:22

- Microsoft .Net Challenge 2017 par Le Blog (Vert) d'Arnaud JUND le 01-30-2017, 15:25

- Office 365: Utiliser le bouton Export to Excel depuis un teamsite SharePoint Online et avec le client Office 2007 par Blog Technique de Romelard Fabrice le 01-27-2017, 18:58