LINQ: ninguno contra todos no

A menudo quiero verificar si un valor provisto coincide con uno en una lista (por ejemplo, al validar):

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

Recientemente, noté que ReSharper me pidió que simplificara estas consultas para:

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

Obviamente, esto es lógicamente idéntico, tal vez un poco más legible (si has hecho muchas matemáticas), mi pregunta es: ¿esto resulta en un golpe de rendimiento?

Se siente como debería (es decir. .Any() suena como un cortocircuito, mientras que .All() parece que no), pero no tengo nada que corrobore esto. ¿Alguien tiene un conocimiento más profundo sobre si las consultas resolverán lo mismo, o si ReSharper me está llevando por mal camino?

Implementación de All según ILSpy (como en realidad fui y miré, en lugar del “bueno, ese método funciona un poco como …” Podría hacerlo si estuviéramos discutiendo la teoría en lugar del impacto).

 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; } 

Implementación de Any según 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; } 

Por supuesto, podría haber alguna diferencia sutil en la IL producida. Pero no, no, no hay. La IL es más o menos la misma, pero por la inversión obvia de devolver verdadero en la coincidencia de predicados versus devolver falsa en el desajuste de predicados.

Esto es linq-for-solo, por supuesto. Es posible que algún otro proveedor de linq trate uno mucho mejor que el otro, pero si ese fuera el caso, es bastante aleatorio cuál obtuvo la implementación más óptima.

Parecería que la regla se reduce únicamente a alguien que siente que if(determineSomethingTrue) es más simple y más legible que if(!determineSomethingFalse) . Y para ser justos, creo que tienen un poco de sentido porque a menudo encuentro if(!someTest) confuso * cuando hay una prueba alternativa de igual verbosidad y complejidad que volvería a ser cierta para la condición sobre la que queremos actuar. Sin embargo, en realidad, personalmente no encuentro nada que favorezca a una sobre la otra de las dos alternativas que usted ofrece, y tal vez se inclinaría ligeramente hacia la primera si el predicado fuera más complicado.

* No confuso como en “no entiendo”, pero confuso porque me preocupa que haya una razón sutil para la decisión que no entiendo, y se necesitan algunos saltos mentales para darse cuenta de que “no, simplemente decidieron hacerlo”. de esa manera, espera lo que estaba buscando en este código para otra vez … ”

Puede encontrar que estos métodos de extensión hacen que su código sea más legible:

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

Ahora en lugar de tu original

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

tu puedes decir

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

Ambos tendrían un rendimiento idéntico porque ambos detienen la enumeración después de que se pueda determinar el resultado: Any() en el primer elemento que el predicado pasado evalúa como true y All() en el primer elemento que el predicado evalúa como false .

All los cortocircuitos en el primer no juego, por lo que no es un problema.

Un área de sutileza es que

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

Es verdad. Todos los elementos en la secuencia son pares.

Para obtener más información sobre este método, consulte la documentación de Enumerable.All .

Como otras respuestas han sido bien cubiertas: no se trata de rendimiento, se trata de claridad.

Existe un amplio soporte para ambas opciones:

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

Pero creo que esto podría lograr un apoyo más amplio :

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

Simplemente computar el booleano (y nombrarlo) antes de negar algo aclara esto mucho en mi mente.

De acuerdo con este enlace

Cualquiera: comprueba al menos un partido

Todo: comprueba que todo coincida

All() determina si todos los elementos de una secuencia satisfacen una condición.
Any() determina si algún elemento de una secuencia satisface la condición.

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

Si echas un vistazo a la fuente de Enumerable , verás que la implementación de Any and All es bastante cercana:

 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; } 

No hay forma de que un método sea significativamente más rápido que el otro ya que la única diferencia radica en una negación booleana, por lo que prefieren la legibilidad sobre el falso rendimiento.