GQ08 IX: petite optimisation Linq to object


Allez, une rapide pour la fin de journée.
Le truc est simple mais il est important de toujours l’avoir en tête lorsque l’on fait du Linq.

La requête suivante est correcte mais peut-être optimisée. Comment ?

if (query.Count() == 0) { //... }


Dans le même genre:

if ((from c in customers where c.City == "Paris" select c).Count() != 0) { }
Comments (11)

  1. en utilisant Any à la place de Count() != 0 :

    if ((from c in customers

        where c.City == "Paris"

        select c).Any())

    En effet, le Any va ârrêter l’itération dès qu’il va en trouver un alors que le Count va passer tous les éléments.

  2. Olivier says:

    On peut remplacer le count qui itère par un test sur First :

    if (q.First()==null) … est équivalent à Count==0

    la même chose peut s’appliquer à la seconde requête ou bien c’est un piège ? 🙂

  3. Olivier says:

    On peut remplacer le count qui itère par un test sur First :

    if (q.First()==null) … est équivalent à Count==0

    la même chose peut s’appliquer à la seconde requête ou bien c’est un piège ? 🙂

  4. Olivier, je ne suis pas d’accord. Au minimum, il faut utiliser FirstOrDefault(). First retournera une exception s’il n’y a aucun élément.

  5. Olivier says:

    Matthieu ton observation est à la fois juste et fausse 🙂

    Juste car si la liste est faite d’objets dont la valeur par défaut est "null" utiliser FirstOrDefault est plus élégant que d’utiliser First comme je le montrais, ce qui aurait obligé dans un code réellement fonctionnel à tester l’exception (ce que j’ai zappé car là n’était pas vraiment la question du quizz à mon sens).

    Mais ta remarque est fausse car si les objets de la liste ont vraiment une valeur par défaut (différente de "null") alors le test retourne le contraire de ce qui est attendu, ce qui est pour le moins facheux !

    Exemple :

    List<int> liste = new List<int>(0);

    Console.WriteLine((q.Count()==0));

    Console.WriteLine(q.FirstOrDefault()==null);

    hélas, si la première ligne retourne bien True, la seconde retourne False !

    Les int ont pour valeur par défaut "0" et non null.

    De ce fait, ton objection ne m’apparaît pas fondée, car le test sur l’exception mis à part (et ajouté dans un code réel) l’utilisation de First est "générique" et produit _toujours_ le résultat escompté alors que ta version réclamera de modifier le test en fonction de la valeur par défaut du type considéré, ce qui peut être la porte ouverte à un bug sournois découvert tardivement, le jour où on passe au code une liste d’éléments dont la valeur par défaut n’est pas "null"…

    Certes on pinaille un peu, mais vu que nous ne sommes que deux sur ce quizz, on a la place pour discuter et ça ne devrait gêner personne 🙂

  6. thierry béhin says:

    J’ai déjà rencontré le probleme sur le count() (encore pire j’avais fait un count(*)>0 la requete remontait tous les champs alors que le count() suffisait), effectivement niveau perf c’est à proscrire, vive le any().

    trés bonne remarque qui sauvera la vie de bien des applications.

  7. @ Olivier. Je suis d’accord avec toi sur le fait que le FirstOrDefault n’est pas la bonne solution. C’est pour ça que j’avais dit "Au minimum".

    Cependant, je trouve qu’il est abérant de tester sur la levée d’une exception. Les exceptions ne doivent pas être utilisées dans des cas "normaux". De plus, ce n’est pas performant car la levée d’une exception est une opération longue. Par conséquent, cela dégradera les performances. Donc dans le cas de Mitsu, je pense définitivement qu’il faut utiliser la méthode Any.

  8. Olivier says:

    @ Matthieu. Le côté "coûteux" de la gestion des exceptions est un débat qui existe depuis Java et Delphi. Il y a en effet le clan de ceux qui disent comme toi "hou lala ! les exceptions c’est super lents donc il ne faut jamais s’en servir" et le clan opposé des fanatiques qui pensent l’inverse.

    Personnellement je préfère la position plus centrale: Dans la réalité je n’ai jamais vu une application (java, delphi ou C#) "ramer" à cause d’une gestion d’exception, c’est un mythe à mon sens du même ordre que celui qui veut que sous .NET il faut systématiquement utiliser un StringBuilder pour faire une concaténation de chaînes.

    Pour ce dernier cas j’ai d’ailleurs fait de nombreux tests et dans la majorité de ceux-ci, et contre la "pensée unique" de l’instant, une concaténation est plus rapide qu’un StringBuilder… Il en va de même des exceptions et je n’agréé donc pas ta position qui consiste en quelque sorte à les diaboliser.

    Sauf dans de très rares cas se priver des exceptions ne se justifie donc pas vraiment.

    Ici, sauf à montrer que le count est appelé dans une boucle de plusieurs milliers d’itérations, je pense que gérer une exception n’a strictement aucun impact sur les performances de l’application considérée.

    Mais bon, l’utilisation de Any n’est pas une mauvaise idée non plus, c’est une autre façon de tester s’il existe un élément comme First.

    Mais on pinaille encore 🙂

    Quoi que le sujet soit très intéressant.

  9. Olivier says:

    L’association des mots "Mythe" et de "StringBuilder" dans une phrase de mon intervention plus haut ici m’a valu quelques remarques de lecteurs, afin de préciser le fond de ma pensée sur une question essentielle : la qualité d’un logiciel, je renvoie ceux que la question intéressent à ce billet que je viens d’écrire : http://www.e-naxos.com/Blog/post.aspx?id=166b0ef8-9323-47a3-ae49-26ed39deafac

    bonne lecture ! 🙂

Skip to main content