Dans mon code, j'ai besoin d'utiliser IEnumerable<>
plusieurs fois ainsi obtenir l'erreur Resharper de "Énumération multiple possible 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 pour êtreList
puis éviter l'énumération multiple possible mais je n'obtiens pas l'objet le plus élevé que je puisse gérer. - Une autre chose que je peux faire est de convertir
IEnumerable
àList
au début de la méthode:
public List<object> Foo(IEnumerable<object> objects)
{
var objectList = objects.ToList();
// ...
}
Mais c'est juste gênant .
Que feriez-vous dans ce scénario?
la source
IReadOnlyCollection(T)
(nouveau avec .net 4.5) comme la meilleure interface pour transmettre l'idée qu'il s'agit d'unIEnumerable(T)
qui est destiné à être énuméré plusieurs fois. Comme l'indique cette réponse,IEnumerable(T)
elle-même est si générique qu'elle peut même faire référence à un contenu non réinitialisable qui ne peut pas être énuméré à nouveau sans effets secondaires. MaisIReadOnlyCollection(T)
implique une réitération..ToList()
au début de la méthode, vous devez d'abord vérifier si le paramètre n'est pas nul. Cela signifie que vous obtiendrez deux endroits où vous lancerez une ArgumentException: si le paramètre est nul et s'il ne contient aucun élément.IReadOnlyCollection<T>
vsIEnumerable<T>
, puis j'ai posté une question sur l'utilisation enIReadOnlyCollection<T>
tant que paramètre et dans quels cas. Je pense que la conclusion à laquelle j'ai finalement abouti est la logique à suivre. Qu'il devrait être utilisé là où l'énumération est prévue, pas nécessairement là où il pourrait y avoir une énumération multiple.Si vos données vont toujours être reproductibles, ne vous en faites peut-être pas. Cependant, vous pouvez également le dérouler - cela est particulièrement utile si les données entrantes peuvent être volumineuses (par exemple, lecture à partir du disque / réseau):
Remarque J'ai changé un peu la sémantique de DoSomethingElse, mais c'est principalement pour montrer l'utilisation non déroulée. Vous pouvez reconditionner l'itérateur, par exemple. Vous pouvez également en faire un bloc itérateur, ce qui pourrait être bien; alors il n'y a pas
list
- et vous feriezyield return
les articles tels que vous les obtenez, plutôt que de les ajouter à une liste à retourner.la source
L'utilisation de
IReadOnlyCollection<T>
ouIReadOnlyList<T>
dans la signature de méthode au lieu deIEnumerable<T>
, a l'avantage de rendre explicite le fait que vous devrez peut-être vérifier le nombre avant l'itération ou répéter plusieurs fois pour une autre raison.Cependant, ils ont un énorme inconvénient qui causera des problèmes si vous essayez de refactoriser votre code pour utiliser des interfaces, par exemple pour le rendre plus testable et plus convivial pour le proxy dynamique. Le point clé est qu'il
IList<T>
n'hérite pasIReadOnlyList<T>
et de la même manière pour les autres collections et leurs interfaces en lecture seule respectives. (En bref, cela est dû au fait que .NET 4.5 voulait conserver la compatibilité ABI avec les versions antérieures. Mais ils n'ont même pas profité de l'occasion pour changer cela dans .NET core. )Cela signifie que si vous obtenez un
IList<T>
d'une partie du programme et que vous souhaitez le passer à une autre partie qui attend unIReadOnlyList<T>
, vous ne pouvez pas! Vous pouvez cependant passer unIList<T>
comme unIEnumerable<T>
.Au final,
IEnumerable<T>
c'est la seule interface en lecture seule prise en charge par toutes les collections .NET, y compris toutes les interfaces de collection. Toute autre alternative reviendra vous mordre lorsque vous réaliserez que vous vous êtes exclu de certains choix d'architecture. Je pense donc que c'est le type approprié à utiliser dans les signatures de fonction pour exprimer que vous voulez juste une collection en lecture seule.(Notez que vous pouvez toujours écrire une
IReadOnlyList<T> ToReadOnly<T>(this IList<T> list)
méthode d'extension qui effectue une conversion simple si le type sous-jacent prend en charge les deux interfaces, mais vous devez l'ajouter manuellement partout lors de la refactorisation, où asIEnumerable<T>
est toujours compatible.)Comme toujours, ce n'est pas un absolu, si vous écrivez du code lourd de base de données où une énumération multiple accidentelle serait un désastre, vous préférerez peut-être un compromis différent.
la source
Si le but est vraiment d'empêcher plusieurs énumérations, la réponse de Marc Gravell est celle à lire, mais en conservant la même sémantique, vous pouvez simplement supprimer le redondant
Any
et lesFirst
appels et aller avec:Notez que cela suppose que vous n'êtes
IEnumerable
pas générique ou du moins contraint à être un type de référence.la source
objects
est toujours transmis à DoSomethingElse qui retourne une liste, donc la possibilité que vous êtes encore en train d'énumérer deux fois est toujours là. Dans les deux cas, si la collection était une requête LINQ to SQL, vous frappez deux fois la base de données.First
lève une exception pour une liste vide. Je ne pense pas qu'il soit possible de dire de ne jamais lancer d'exception sur une liste vide ou de toujours lancer une exception sur une liste vide. Ça dépend ...Je surcharge généralement 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 à IEnumerable effectuera un .ToList ().
Le programmeur peut choisir de .ToList () à un niveau supérieur si plusieurs opérations sont concaténées, puis appeler la surcharge IList ou laisser ma surcharge IEnumerable s'en occuper.
la source
Si vous avez seulement besoin de vérifier le premier élément, vous pouvez y jeter un œil sans itérer toute la collection:
la source
Tout d'abord, cet avertissement ne signifie pas toujours autant. Je l'ai généralement désactivé après m'être assuré qu'il ne s'agissait pas d'un col de bouteille performant. Cela signifie simplement que le
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 beaucoup de temps, dans ce cas, vous n'utilisez qu'un seul élément la première fois.Dans ce scénario, vous pouvez également exploiter encore plus les puissantes méthodes d'extension linq.
Il est possible d'évaluer la
IEnumerable
seule fois dans ce cas avec quelques tracas, mais profilez d'abord et voyez si c'est vraiment un problème.la source
IEnumerables
qui donne des résultats différents chaque fois que vous l'itérez ou qui prend beaucoup de temps pour l'itérer. Voir la réponse de Marc Gravells - Il déclare également avant tout "peut-être ne vous inquiétez pas". Le truc, c'est que la plupart du temps vous voyez cela, vous n'avez rien à craindre.