c# - que - resharper visual studio 2017 full
LINQ: No ninguno vs todos no (8)
Ambos tendrían un rendimiento idéntico porque ambos pueden detener 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
.
A menudo quiero verificar si un valor proporcionado coincide con uno en una lista (por ejemplo, al validar):
if (!acceptedValues.Any(v => v == someValue))
{
// exception logic
}
Recientemente, he notado que ReSharper me pide que simplifique estas consultas para:
if (acceptedValues.All(v => v != someValue))
{
// exception logic
}
Obviamente, esto es lógicamente idéntico, quizás un poco más legible (si has hecho muchas matemáticas), mi pregunta es: ¿esto resulta en un impacto en el rendimiento?
Se siente como si debiera (es decir, .Any()
suena como si fuera un cortocircuito, mientras que .All()
suena como si no lo hiciera), pero no tengo nada que justifique esto. ¿Alguien tiene un conocimiento más profundo sobre si las consultas se resolverán igual o si ReSharper me está desviando?
Como ya se han cubierto otras respuestas: no se trata de rendimiento, se trata de claridad.
Hay 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, lo aclaro mucho en mi mente.
Es posible que encuentre que estos métodos de extensión hacen que su código sea más legible:
public static bool None<TSource>(this IEnumerable<TSource> source)
{
return !source.Any();
}
public static bool None<TSource>(this IEnumerable<TSource> source,
Func<TSource, bool> 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
}
Implementación de All
acuerdo con ILSpy (como en realidad fui y miré, en lugar de "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<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> 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<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> 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. El IL es prácticamente el mismo, pero para la inversión obvia de devolver verdadero en el emparejamiento de predicado versus devolver falso en el desajuste de predicado.
Esto es, por supuesto, linq-for-objetos. Es posible que algún otro proveedor de linq trate a uno mucho mejor que al otro, pero si ese fuera el caso, es bastante aleatorio cuál tiene 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 punto en el sentido de que a menudo encuentro if(!someTest)
confuso * cuando hay una prueba alternativa de verbosidad y complejidad iguales que se volvería verdadera para la condición en la que queremos actuar. Sin embargo, en realidad, personalmente no encuentro nada que favorezca a una sobre la otra de las dos alternativas que da, y quizás me incline ligeramente hacia la primera si el predicado fuera más complicado.
* No confuso como en No lo entiendo, pero confuso como en 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, espere, ¿qué estaba buscando en este bit de código otra vez? ... "
Según este link
Cualquiera - Comprueba al menos una partida
Todos - Comprueba que todos coinciden
Si echas un vistazo a la fuente Enumerable , verás que la implementación de Any
and All
es bastante cercana:
public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> 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<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> 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, así que prefiera la legibilidad en lugar de la falsa ganancia de rendimiento.
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
All
los cortocircuitos en la primera no coincidencia, por lo que no es un problema.
Un área de sutileza es que
bool allEven = Enumerable.Empty<int>().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 .