La manipulation de l'avertissement d'une possible plusieurs énumération de IEnumerable
Dans mon code dans le besoin d'utiliser un IEnumerable<>
plusieurs fois ainsi obtenir la Resharper erreur de "multiples énumération de IEnumerable
".
Exemple de code:
public List<object> Foo(IEnumerable<object> objects)
{
if (objects == null || !objects.Any())
throw new ArgumentException();
var firstObject = objects.First();
var list = DoSomeThing(firstObject);
var secondList = DoSomeThingElse(objects);
list.AddRange(secondList);
return list;
}
- Je peux changer le
objects
paramètre àList
et puis éviter les multiples énumération, mais alors je ne comprends pas la plus haute de l'objet que je peux gérer. - Une autre chose que je peux faire est de convertir le
IEnumerable
àList
au début de la méthode:
public List<object> Foo(IEnumerable<object> objects)
{
var objectList = objects.ToList();
//...
}
Mais c'est juste maladroit.
Que feriez-vous dans ce scénario?
Vous devez vous connecter pour publier un commentaire.
Le problème avec la prise
IEnumerable
comme un paramètre, c'est qu'il indique appelants "je tiens à énumérer ce". Il ne dit pas quel est le nombre de fois que vous le souhaitez à énumérer.L'objectif de la prise la plus haute de l'objet est noble, mais il laisse de la place pour un trop grand nombre d'hypothèses. Voulez-vous vraiment quelqu'un pour passer un LINQ to SQL de la requête à cette méthode, seulement pour vous les énumérer deux fois (potentiellement des résultats différents à chaque fois?)
La sémantique qui manque ici, c'est que l'appelant, qui n'a peut-être pas prendre le temps de lire les détails de la méthode, peuvent supposer que vous seulement itérer une fois, de sorte qu'ils vous passent un cher objet. Votre signature de la méthode n'indique pas de toute façon.
Par la modification de la signature de la méthode de
IList
/ICollection
, vous aurez au moins le rendre plus clair pour l'appelant quelles sont vos attentes, et ils peuvent éviter des erreurs coûteuses.Sinon, la plupart des développeurs à la recherche à la méthode pourrait supposer que vous ne itérer une fois. Si vous prenez un
IEnumerable
est si important, vous devriez envisager de le faire la.ToList()
au début de la méthode.C'est une honte .NET ne dispose pas d'une interface IEnumerable + Count + Indexeur, sans Ajouter/Supprimer etc. les méthodes, ce qui est ce que je soupçonne, il permettrait de résoudre ce problème.
IReadOnlyCollection(T)
(de nouveaux avec de .net 4.5) comme la meilleure interface pour transmettre l'idée que c'est unIEnumerable(T)
qui est destiné à être énuméré plusieurs fois. Comme cette réponse unis,IEnumerable(T)
lui-même est tellement générique qu'il peut même se référer à l'onu-resetable contenu qui ne peuvent être énumérées plus de nouveau sans effets secondaires. MaisIReadOnlyCollection(T)
implique de re-iterability..ToList()
au début de la méthode, vous devez d'abord vérifier si le paramètre n'est pas null. Cela signifie que vous obtiendrez deux endroits où vous jetez un ArgumentException: si le paramètre est null, et si elle n'a pas tous les éléments.IReadOnlyCollection<T>
vsIEnumerable<T>
et puis publié en ligne question sur l'utilisation deIReadOnlyCollection<T>
comme paramètre, et dans quels cas. Je pense que le conclusion je suis finalement arrivé à la logique à suivre. Qu'il doit être utilisé lorsque l'énumération est attendu, pas forcément là où il pourrait y avoir plusieurs énumération.Si vos données est toujours va être reproductible, peut-être ne vous inquiétez pas à ce sujet. Cependant, vous pouvez dérouler trop - ce qui est particulièrement utile si les données entrantes pourrait être de grande taille (par exemple, la lecture à partir du disque/réseau):
Remarque j'ai changé la sémantique de DoSomethingElse un peu, mais c'est surtout pour montrer déroulé d'utilisation. Vous pourriez re-wrap l'itérateur, par exemple. Vous pourriez faire un itérateur bloc de trop, ce qui pourrait être sympa; alors il n'y a pas de
list
- et vousyield return
les éléments que vous les obtenez, plutôt que de l'ajouter à une liste à renvoyer.À l'aide de
IReadOnlyCollection<T>
ouIReadOnlyList<T>
dans la signature de la méthode au lieu deIEnumerable<T>
, a l'avantage de rendre plus explicite le fait que vous pourriez avoir besoin de vérifier le décompte avant l'itération, ou pour itérer plusieurs fois pour une autre raison.Cependant, ils ont un énorme inconvénient qui peut poser des problèmes si vous essayez de revoir votre code pour utiliser les interfaces, par exemple pour le rendre plus testable et convivial à la dynamique de l'utilisation de proxy. Le point clé est que
IList<T>
n'hérite pas deIReadOnlyList<T>
, et de même pour les autres collections et de leur lecture seule interfaces. (En bref, c'est parce que .NET 4.5 voulais garder ABI compatibilité avec les versions antérieures. Mais ils ne prennent même pas la possibilité de changer cela dans .NET de base.)Cela signifie que si vous obtenez une
IList<T>
d'une partie du programme et que vous souhaitez passer à une autre partie qui s'attend à uneIReadOnlyList<T>
, vous ne pouvez pas! Cependant, vous pouvez transmettre unIList<T>
comme unIEnumerable<T>
.En fin de compte,
IEnumerable<T>
est la seule lecture seule interface pris en charge par tous .NET des collections y compris la collection de toutes les interfaces. Toute autre alternative va revenir à vous mordre que vous vous rendez compte que vous avez verrouillé vous-même hors de certains choix de l'architecture. Donc, je pense que c'est le bon type à utiliser dans les signatures de la fonction d'exprimer que vous voulez juste une collection en lecture seule.(Notez que vous pouvez toujours écrire un
IReadOnlyList<T> ToReadOnly<T>(this IList<T> list)
méthode d'extension simple jette si le type sous-jacent prend en charge les deux interfaces, mais vous devez l'ajouter manuellement partout lors d'un refactoring, où queIEnumerable<T>
est toujours compatibles.)Comme toujours, ce n'est pas un absolu, si vous êtes à l'écriture de la base de données-lourds de code où les multiples énumération serait une catastrophe, vous pourriez préférer un autre compromis.
Si le but est vraiment d'éviter que plusieurs énumérations que la réponse de Marc Gravel est le seul à lire, mais en conservant la même sémantique que vous pourriez simple de supprimer la redondant
Any
etFirst
appels et aller avec:Remarque, que cela suppose que vous
IEnumerable
n'est pas générique ou, au moins, est contraint d'être un type de référence.objects
est encore passé à DoSomethingElse qui est de retour d'une liste, de sorte que la possibilité que vous êtes toujours à l'énumération deux fois est toujours là. De toute façon si la collection a été un LINQ to SQL de la requête, vous a frappé le DB deux fois.First
va lever une exception si la liste est vide. Je ne pense pas qu'il est possible de dire de ne jamais jeter d'exception sur la liste vide ou toujours jeter l'exception sur la liste est vide. Ça dépend...J'ai l'habitude de surcharger ma méthode avec IEnumerable et IList dans cette situation.
Je prends soin d'expliquer dans le résumé des commentaires des méthodes que l'appel de IEnumerable effectuera une .ToList().
Le programmeur peut choisir de .ToList() à un niveau plus élevé si plusieurs opérations sont concaténés et ensuite appeler le IList de surcharge ou de laisser mon IEnumerable surcharge de prendre soin de cela.
Si vous avez uniquement besoin de vérifier le premier élément que vous pouvez coup d'oeil sur elle sans une itération à l'ensemble de la collection:
Tout d'abord, cet avertissement ne signifie pas toujours beaucoup. J'ai l'habitude de désactivé après que vous assurer que ce n'est pas une performance goulot de bouteille. Il veut simplement dire que l'
IEnumerable
est évalué deux fois, ce qui n'est généralement pas un problème, sauf si leevaluation
lui-même prend beaucoup de temps. Même si cela prend du temps, dans ce cas, votre seulement en utilisant un élément de la première fois autour.Dans ce scénario, vous pourrait aussi exploiter la puissante extension linq méthodes encore plus.
Il est possible d'évaluer les
IEnumerable
une fois, dans ce cas, avec quelques soucis, mais le profil de la première et de voir si c'est vraiment un problème.IEnumerables
qui donne des résultats différents à chaque fois que vous la répéter ou prendre un temps très long pour itérer. Voir Marc Gravells réponse - Il également de l'état d'abord et avant tout "peut-être ne vous inquiétez pas". La chose est Beaucoup de la fois que vous voyez cela, vous n'avez rien à craindre.ICollection<T>
(ou peut-être la nouvelleIReadOnlyCollection<T>
), à quel point il devient plus sûr de supposer que ce n'est pas un paresseusement évalué énumérable. Si ce n'est pas la garantie d'être reproductible, j'irais pour Marc Gravel de l'approche de l'orthographe lesGetEnumerator
/MoveNext
/Current
accès.