LINQ: pas un vs tous ne pas

Souvent, je veux vérifier si une valeur fournie correspond à une valeur dans une liste (par exemple lors de la validation):

if (!acceptedValues.Any(v => v == someValue)) { // exception logic } 

Récemment, j’ai remarqué que ReSharper me demandait de simplifier ces requêtes pour:

 if (acceptedValues.All(v => v != someValue)) { // exception logic } 

Evidemment, ceci est logiquement identique, peut-être légèrement plus lisible (si vous avez fait beaucoup de mathématiques), ma question est la suivante: cela se traduit-il par un impact sur la performance?

On se croirait comme ça (c.-à-d .Any() ressemble à un court-circuit, tandis que .All() sonne comme si ce n’était pas le cas), mais je n’ai rien à prouver. Est-ce que quelqu’un a une connaissance plus approfondie de la question de savoir si les requêtes vont résoudre le même problème, ou si ReSharper me conduit en erreur?

Mise en œuvre de All selon ILSpy (comme en fait je suis allé et regardé, plutôt que le “bien, cette méthode fonctionne un peu comme …” Je pourrais faire si nous discutions de la théorie plutôt que de l’impact).

 public static bool All(this IEnumerable source, Func predicate) { if (source == null) { throw Error.ArgumentNull("source"); } if (predicate == null) { throw Error.ArgumentNull("predicate"); } foreach (TSource current in source) { if (!predicate(current)) { return false; } } return true; } 

Implémentation de Any selon ILSpy:

 public static bool Any(this IEnumerable source, Func predicate) { if (source == null) { throw Error.ArgumentNull("source"); } if (predicate == null) { throw Error.ArgumentNull("predicate"); } foreach (TSource current in source) { if (predicate(current)) { return true; } } return false; } 

Bien sûr, il pourrait y avoir une différence subtile dans la production IL. Mais non, non, il n’y en a pas. L’IL est à peu près la même chose, mais pour l’inversion évidente du retour de true sur la correspondance du prédicat et du retour de false sur la non-concordance du prédicat.

Ce n’est bien sûr que linq-for-objects. Il est possible qu’un autre fournisseur de linq en traite beaucoup mieux que l’autre, mais si tel était le cas, il serait plutôt aléatoire de choisir l’implémentation la plus optimale.

Il semblerait que la règle revient uniquement à quelqu’un qui a le sentiment que if(determineSomethingTrue) quelque chose if(determineSomethingTrue) est plus simple et plus lisible que if(!determineSomethingFalse) Determine quelque chose if(!determineSomethingFalse) . Et pour être honnête, je pense qu’ils ont un point dans le sens où je trouve souvent if(!someTest) déroutant * quand il y a un test alternatif de verbosité et de complexité égales qui serait vrai pour la condition sur laquelle nous voulons agir. Pourtant, en réalité, je ne trouve rien pour favoriser l’une des deux alternatives que vous proposez, et je m’appuierais peut-être très légèrement sur le premier si le prédicat était plus compliqué.

* Pas déroutant comme dans je ne comprends pas, mais déroutant comme dans je crains qu’il y ait une raison subtile pour la décision que je ne comprends pas, et il faut quelques sauts mentaux pour se rendre compte que “non, ils ont juste décidé de faire c’est comme ça, attendez ce que je regardais encore pour ce morceau de code? … ”

Vous pourriez trouver que ces méthodes d’extension rendent votre code plus lisible:

 public static bool None(this IEnumerable source) { return !source.Any(); } public static bool None(this IEnumerable source, Func predicate) { return !source.Any(predicate); } 

Maintenant, au lieu de votre original

 if (!acceptedValues.Any(v => v == someValue)) { // exception logic } 

Tu pourrais dire

 if (acceptedValues.None(v => v == someValue)) { // exception logic } 

Les deux auraient des performances identiques car les deux arrête l’énumération après que le résultat peut être déterminé – Any() sur le premier élément évalué par true pour le prédicat passé et All() sur le premier élément évalué par false .

All courts-circuits sur le premier non-match, donc ce n’est pas un problème.

Un domaine de subtilité est que

  bool allEven = Enumerable.Empty().All(i => i % 2 == 0); 

Est vrai. Tous les éléments de la séquence sont pairs.

Pour plus d’informations sur cette méthode, consultez la documentation de Enumerable.All .

Comme d’autres réponses l’ont bien couvert: il ne s’agit pas de performance, mais de clarté.

Il existe un large support pour vos deux options:

 if (!acceptedValues.Any(v => v == someValue)) { // exception logic } if (acceptedValues.All(v => v != someValue)) { // exception logic } 

Mais je pense que cela pourrait atteindre un soutien plus large :

 var isValueAccepted = acceptedValues.Any(v => v == someValue); if (!isValueAccepted) { // exception logic } 

Simplement calculer le booléen (et le nommer) avant de nier quoi que ce soit efface tout cela dans ma tête.

Selon ce lien

Any – Vérifie au moins un match

Tous – Vérifie que tous correspondent

All() détermine si tous les éléments d’une séquence satisfont à une condition.
Any() détermine si un élément d’une séquence satisfait la condition.

 var numbers = new[]{1,2,3}; numbers.All(n => n % 2 == 0); // returns false numbers.Any(n => n % 2 == 0); // returns true 

Si vous regardez la source Enumerable, vous verrez que l’implémentation de Any et All est assez proche:

 public static bool Any(this IEnumerable source, Func predicate) { if (source == null) throw Error.ArgumentNull("source"); if (predicate == null) throw Error.ArgumentNull("predicate"); foreach (TSource element in source) { if (predicate(element)) return true; } return false; } public static bool All(this IEnumerable source, Func predicate) { if (source == null) throw Error.ArgumentNull("source"); if (predicate == null) throw Error.ArgumentNull("predicate"); foreach (TSource element in source) { if (!predicate(element)) return false; } return true; } 

Il n’y a pas moyen qu’une méthode soit significativement plus rapide que l’autre puisque la seule différence réside dans une négation booléenne. Préférez donc la lisibilité sur les fausses performances gagnées.