37 votes

Resharper : Enumération multiple possible de IEnumerable

J'utilise la nouvelle version 6 de Resharper. À plusieurs endroits dans mon code, il a souligné du texte et m'a averti qu'il pouvait y avoir un problème de sécurité. Énumération multiple possible de IEnumerable .

Je comprends ce que cela signifie, et j'ai suivi les conseils le cas échéant, mais dans certains cas, je ne suis pas sûr que ce soit vraiment un problème.

Comme dans le code suivant :

var properties = Context.ObjectStateManager.GetObjectStateEntry(this).GetModifiedProperties();
if (properties.Contains("Property1") || properties.Contains("Property2") || properties.Contains("Property3")) {
    ...
}

C'est souligner chaque mention de properties sur la deuxième ligne, avertissant que je suis en train d'énumérer sur ce IEnumerable plusieurs fois.

Si j'ajoute .ToList() à la fin de la ligne 1 (en tournant properties d'un IEnumerable<string> à un List<string> ), les avertissements disparaissent.

Mais sûrement, si je le convertis en une liste, alors il va énumérer sur l'IEnumerable entier pour construire la liste en premier lieu, et ensuite énumérer sur la liste comme requis pour trouver les propriétés (c'est-à-dire 1 énumération complète, et 3 énumérations partielles). Alors que dans mon code original, il ne fait que les 3 énumérations partielles.

Ai-je tort ? Quelle est la meilleure méthode ici ?

41voto

Jon Skeet Points 692016

Je ne sais pas exactement ce que votre properties mais s'il s'agit essentiellement de représenter une requête de base de données non matérialisée, alors votre if va effectuer trois requêtes.

I suspect qu'il serait préférable de faire :

string[] propertiesToFind = { "Property1", "Property2", "Property3" };
if (properties.Any(x => propertiesToFind.Contains(x))
{
     ...
}

Ce sera logiquement n'itère qu'une seule fois sur la séquence - et si une requête de base de données est impliquée, il est possible d'utiliser une clause SQL "IN" pour tout faire dans la base de données en une seule requête.

6voto

jishi Points 10442

Si vous invoquez Contains() sur un IEnumerable, il invoquera la méthode d'extension qui se contentera d'itérer parmi les éléments afin de le trouver. IList a une réelle mise en œuvre pour Contains() qui sont probablement plus efficaces qu'une itération régulière à travers les valeurs (il pourrait y avoir un arbre de recherche avec des hachages ?), c'est pourquoi il n'avertit pas avec IList .

Puisque la méthode d'extension saura seulement qu'il s'agit d'une IEnumerable il ne peut probablement pas utiliser de méthodes intégrées pour le traitement de l'information. Contains() même s'il serait possible en théorie d'identifier les types connus et de les couler en conséquence afin de les utiliser.

1voto

sehe Points 123151

C'est un avertissement. L'énumération trois fois pour des cibles de recherche différentes sera probablement beaucoup plus lente (sauf si vous connaître que vous ne cherchez que les trois premiers éléments (ou à peu près).

Faites ceci

var propSet = HashSet<string>(properties);
if (propSet.Contains("Property1")
 || propSet.Contains("Property2")
 || propSet.Contains("Property3")) {

Il sera bien plus performant. Si vous savez qu'il s'agit de la première n éléments, properties.Take(n) est votre ami

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