267 votes

LINQ: pas un vs tous ne pas

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

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

Récemment, j'ai remarqué ReSharper de me demander de simplifier ces demandes de renseignements à:

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

Évidemment, c'est logiquement identique, peut-être un peu plus lisible (si vous avez fait beaucoup de mathématiques), ma question est: est-ce le fruit dans un gain de performance?

Il se sent comme il se doit (c'est à dire .Any() sons comme des court-circuits, alors que .All() sons comme elle n'a pas), mais je n'ai rien pour le prouver. Quelqu'un aurait-il une connaissance plus approfondie de savoir si les requêtes permettra de résoudre le même, ou si ReSharper est ce qui m'a égarée?

338voto

Jon Hanna Points 40291

La mise en œuvre de l' All selon ILSpy (en fait je suis allé chercher, plutôt que de la "ainsi, 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<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;
}

La mise en œuvre de l' Any selon 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;
}

Bien sûr, il peut y avoir des différence subtile dans le IL produit. Mais non, non il n'y en a pas. IL est à peu près le même, mais pour l'évidente inversion de retourner true sur prédicat match contre le retour de faux sur le prédicat d'incompatibilité.

C'est linq-pour-les objets de cours. Il est possible que certains autres linq fournisseur traite beaucoup mieux que les autres, mais si c'était le cas, c'est assez aléatoire qui a eu le plus d'une mise en œuvre optimale.

Il semblerait que la règle revient uniquement à quelqu'un qui se sent qu' if(determineSomethingTrue) est plus simple et plus lisible qu' if(!determineSomethingFalse). Et, en toute équité, je pense qu'ils ont un peu un point que je trouve souvent if(!someTest) déroutant* quand il y a un test de remplacement de l'égalité des commentaires et de la complexité qui retourne vrai pour l'état que nous voulons agir. Pourtant, vraiment, personnellement, je ne trouve rien de favoriser l'une ou l'autre des deux alternatives que vous donnez, et serait peut-être maigre, très légèrement vers l'ancien si le prédicat sont plus compliquées.

*Ne pas confondre comme je ne comprends pas, mais à confusion, comme dans j'ai peur qu'il y ait quelques subtiles raison de la décision que je ne comprends pas, et il faut quelques mentale saute à réaliser que "non, ils ont juste décidé de faire de cette façon, attendre qu'étais-je en regardant ce morceau de code pour de nouveau?..."

53voto

AakashM Points 32891

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

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

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
}
 

24voto

BrokenGlass Points 91618

Les deux ont 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 le prédicat passé à true et All() sur le premier item le prédicat est évalué à false .

20voto

Anthony Pegram Points 58528

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<int>().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 .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X