LINQ: Ninguno frente a todos No

Resuelto Mark asked hace 12 años • 8 respuestas

A menudo quiero comprobar si un valor proporcionado coincide con uno de 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, quizás un poco más legible (si ha hecho muchas matemáticas), mi pregunta es: ¿esto resulta en un impacto en el rendimiento?

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

Mark avatar Jan 27 '12 07:01 Mark
Aceptado

Implementación de Allacuerdo 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 Anysegú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 el IL producido. Pero no, no, no lo hay. El IL es prácticamente el mismo, excepto por la inversión obvia de devolver verdadero en caso de coincidencia de predicados versus devolver falso en caso de discrepancia de predicados.

Por supuesto, esto es sólo linq para 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 obtuvo la implementación más óptima.

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

*No confuso como en No entiendo, pero confuso como en Me preocupa que haya alguna razón sutil para la decisión que no entiendo, y se necesitan algunos saltos mentales para darme cuenta de que "no, simplemente decidieron hacerlo". es de esa manera, espera, ¿para qué estaba mirando este fragmento de código otra vez?..."

Jon Hanna avatar Jan 27 '2012 00:01 Jon Hanna

Es posible que estos métodos de extensión hagan 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
}
AakashM avatar Jan 27 '2012 09:01 AakashM

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

BrokenGlass avatar Jan 27 '2012 00:01 BrokenGlass

Allcortocircuitos en el primer no partido, 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 de la secuencia son pares.

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

Anthony Pegram avatar Jan 27 '2012 00:01 Anthony Pegram