[Réponse] GQ08 IX: petite optimisation Linq to object

Commençons par une réponse 'fonctionnelle'.

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

nous recherchons ici à déterminer si query est une séquence non nulle, autrement dit, si query renvoie des éléments. Techniquement '.Count() == 0' répond à la question mais est assez coûteux. En effet, .Count() parcourt l'ensemble de la séquence et dans notre cas le nombre total d'éléments nous importe peu. Du coup, initialiser la séquence et tester la présence du premier élément suffit bien évidemment à résoudre la question beaucoup plus efficacement.

 query.GetEnumerator().MoveNext()

'true' signifie que la séquence n'est pas nulle et rien ne sert d'aller plus rien. 'false', la séquence est nulle.

Une méthode Linq fait exactement ce test: query.Any()

Pour répondre au commentaires:

.First() peut résoudre la question dans le sens où il soulevera une exception s'il n'y a aucun élément mais en effet la gestion de l'exception est un peu coûteuse pour un test si simple.

Par contre en aucun cas .FirstOrDefault() ne peut répondre à la question. Attardons nous un temps sur ce cas là. Contrairement à .First(), .FirstOrDefault() ne plante pas dans le cas où aucun élément n'est présent dans la séquence mais renvoie la valeur par défaut du type. Pour tous les types référence, la valeur par défaut est 'null'.

Mais Attention !
Récupérer 'null' depuis .FirstOrDefault() est ambigu du point de vu de notre problématique.
En effet, on a tout à fait le droit de renvoyer un élément null depuis une séquence. Imaginons donc une séquence qui renvoit un unique élément null:

 private static IEnumerable<string> Test()
{
    yield return null;
}

Dans le cas ci-dessus 'Test().FirstOrDefault()' renvoie null mais 'Test().Count()' renvoie 1.

 private static IEnumerable<string> Test()
{
    return new string[] { };
}

Dans ce dernier cas 'Test().FirstOrDefault()' renvoie également null mais 'Test().Count()' renvoie 0...

Dernière petite information non négligeable:

.Count() est une méthode qui renvoie le nombre d'éléments dans une séquence. Cette méthode d'extension fonctionne avec n'importe quel IEnumerable.

La logique de base de .Count() est d'itérer sur l'ensemble de la séquence en incrémentant un compteur. Cette implémentation fonctionne de manière universelle avec n'importe quel IEnumerable. MAIS ! parfois (voire souvent :-) ), la source de la requête est une collection.
Si la source est une pure énumération, ok, il n'y a pas d'autre choix que de compter un à un les éléments, mais si c'est une collection, il est bien entendu plus efficace d'utiliser ICollection.Count (ou .Length dans le cas d'un tableau).

Sachez donc que si votre source est une collection, par exemple List<string>, la méthode .Count() de Linq est optimisée car elle fait le test en interne afin d'utiliser ICollection.Count sans avoir à itérer, sinon, elle itère.