Existe-t-il une raison d'exposer une collection interne en tant que ReadOnlyCollection plutôt que IEnumerable si le code appelant n'itère que sur la collection?
class Bar
{
private ICollection<Foo> foos;
// Which one is to be preferred?
public IEnumerable<Foo> Foos { ... }
public ReadOnlyCollection<Foo> Foos { ... }
}
// Calling code:
foreach (var f in bar.Foos)
DoSomething(f);
Comme je le vois, IEnumerable est un sous-ensemble de l'interface de ReadOnlyCollection et il ne permet pas à l'utilisateur de modifier la collection. Donc, si l'interface IEnumberable est suffisante, c'est celle à utiliser. Est-ce une bonne façon de raisonner à ce sujet ou est-ce que je manque quelque chose?
Merci / Erik
c#
.net
collections
ienumerable
readonly-collection
Erik Öjebo
la source
la source
ReadOnlyCollection
si vous êtes paranoïaque, mais maintenant vous n'êtes pas lié à une implémentation spécifique.Réponses:
Solution plus moderne
À moins que vous n'ayez besoin que la collection interne soit modifiable, vous pouvez utiliser le
System.Collections.Immutable
package, changer votre type de champ pour être une collection immuable, puis l'exposer directement - en supposant qu'elleFoo
est immuable, bien sûr.Réponse mise à jour pour répondre plus directement à la question
Cela dépend de la confiance que vous accordez au code d'appel. Si vous avez un contrôle total sur tout ce qui appellera ce membre et que vous garantissez qu'aucun code ne l'utilisera jamais:
alors bien sûr, aucun mal ne sera fait si vous retournez simplement la collection directement. J'essaye généralement d'être un peu plus paranoïaque que ça.
De même, comme vous le dites: si vous avez seulement besoin
IEnumerable<T>
, alors pourquoi vous attacher à quelque chose de plus fort?Réponse originale
Si vous utilisez .NET 3.5, vous pouvez éviter de faire une copie et éviter le cast simple en utilisant un simple appel à Skip:
(Il existe de nombreuses autres options pour encapsuler trivialement - la bonne chose à propos
Skip
de Select / Where est qu'il n'y a pas de délégué à exécuter inutilement pour chaque itération.)Si vous n'utilisez pas .NET 3.5, vous pouvez écrire un wrapper très simple pour faire la même chose:
la source
ReadOnlyCollection
en anICollection
, puis l'exécutionAdd
réussie. Autant que je sache, cela ne devrait pas être possible. Leevil.Add(...)
devrait générer une erreur. dotnetfiddle.net/GII2jWReadOnlyCollection
- je lance unIEnumerable<T>
qui n'est pas réellement en lecture seule ... c'est au lieu d'exposer unReadOnlyCollection
ReadOnlyCollection
au lieu d'unIEnumerable
, afin de vous protéger contre les maléfices.Si vous avez seulement besoin d'itérer dans la collection:
puis renvoyer IEnumerable suffit.
Si vous avez besoin d'un accès aléatoire aux éléments:
puis enveloppez-le dans ReadOnlyCollection .
la source
System.Collections.Immutable
comme Jon Skeet le recommande est parfaitement valable lorsque vous exposez quelque chose qui ne changera jamais après avoir obtenu une référence. D'un autre côté, si vous exposez une vue en lecture seule d'une collection mutable, alors ce conseil est toujours valide, même si je l'exposerais probablement commeIReadOnlyCollection
(ce qui n'existait pas lorsque j'ai écrit ceci).bar.Foos[17]
avecIReadOnlyCollection
. La seule différence est laCount
propriété supplémentaire .public interface IReadOnlyCollection<out T> : IEnumerable<T>, IEnumerable
bar.Foos[17]
, vous devez l'exposer commeReadOnlyCollection<Foo>
. Si vous voulez connaître la taille et la parcourir, exposez-la sous la formeIReadOnlyCollection<Foo>
. Si vous n'avez pas besoin de connaître la taille, utilisezIEnumerable<Foo>
. Il existe d'autres solutions et d'autres détails, mais cela dépasse le cadre de la discussion de cette réponse particulière.Si vous faites cela, rien n'empêche vos appelants de renvoyer le IEnumerable vers ICollection, puis de le modifier. ReadOnlyCollection supprime cette possibilité, bien qu'il soit toujours possible d'accéder à la collection inscriptible sous-jacente via la réflexion. Si la collection est petite, un moyen sûr et facile de contourner ce problème consiste à renvoyer une copie à la place.
la source
J'évite d'utiliser ReadOnlyCollection autant que possible, c'est en fait beaucoup plus lent que d'utiliser simplement une liste normale. Voir cet exemple:
la source
Parfois, vous voudrez peut-être utiliser une interface, peut-être parce que vous voulez vous moquer de la collection pendant les tests unitaires. Veuillez consulter mon entrée de blog pour ajouter votre propre interface à ReadonlyCollection à l'aide d'un adaptateur.
la source