12
votes

Est-il prudent d'exposer un iNeumable dans une propriété?

Si j'expose un iEnumerable en tant que propriété d'une classe, il y a une possibilité qu'il peut être muté par les utilisateurs d'une classe, et si oui quel est le meilleur moyen de Protection contre la mutation, tout en conservant le type iEnumerable ?


1 commentaires

Il existe des mises en garde tout en utilisant des collections en tant que propriété, veuillez vérifier ce lien de guide de conception de MSDN docs.microsoft.com/en-us/dotnet/tandard/design-Guidelines/.../a>


4 Réponses :


16
votes

Cela dépend de ce que vous retournez. Si vous revenez (disons) une liste mutable , le client peut effectivement le jeter à la liste et la mixte.

Comment vous protégez votre Les données dépendent de ce que vous devez commencer. Readonlycollection est un bon emballage classe, en supposant que vous avez un ilist pour commencer par.

si vos clients ne bénéficieront pas de la valeur de retour de la valeur de retour ilist < / Code> ou Icollection , vous pouvez toujours faire quelque chose comme: xxx

ce qui enveloppe efficacement la collection dans un itérateur. (Il existe différentes manières différentes d'utiliser LINQ pour masquer la source ... bien que cela ne soit pas documenté que les opérateurs cachent la source et qui ne le font pas. Par exemple, appeler Skip (0) < / em> masquer la source dans la mise en œuvre de Microsoft, mais n'est pas documenté pour le faire.)

Sélectionnez définitivement Devrait-il masquer la source.


1 commentaires

Comme d'habitude, Jon Skeet fournit une bonne réponse. J'ai juste besoin d'ajouter que différents types de collecte de données ne sont pas destinés à être utilisés pour la sécurité dans les programmes. Si nous courons dans un environnement de confiance complet, on peut toujours creuser profondément dans nos objets pour trouver tout ce qui est caché à l'intérieur. Nous devons utiliser des mécanismes de sécurité des applications si nous devons vraiment protéger les collections des changements.



1
votes

La collection peut être renvoyée sur le type d'origine et s'il est mutable, il peut ensuite être muté.

Un moyen d'éviter la possibilité que l'original soit muté renvoie une copie de la liste.


2 commentaires

Faire un clone est une solution possible, à droite, mais est-ce vraiment "le meilleur moyen"? Peut-être que parfois c'est, mais parfois pas.


Le retour d'une copie est coûteux pour les grandes collections. Sauf si j'ai vraiment besoin de la sémantique d'une copie, je préfère les emballages comme Jonskeet suggéré.



2
votes

L'utilisateur peut être capable de renvoyer à la classe de collecte, alors exposez.

collection.Select(x => x)


1 commentaires

J'aime ça. Comprenez que si la collection est un type de référence, les éléments des éléments seront toujours mutables, mais changer de cardinalité et de l'ordre de l'encombrement en slurquant dans une liste ou une matrice n'affectera pas la collection d'origine.



1
votes

Je ne suggérerais pas d'envelopper un itératif dans un itérateur pour empêcher les destinataires de monkeying avec la connexion sous-jacente. Mon inclination serait d'utiliser un wrapper quelque chose comme: xxx pré>

si le type de retour des propriétés est ienumerable code>, la coercition de type enracinable code> à iEnumerable code> enregistrerait la structure et rendrait le comportement et la performance correspondent à ceux d'une classe. Si, toutefois, les propriétés, ont été définies comme type de retournement enveloppé code>, il serait alors possible d'enregistrer une étape de boxe dans les cas où le code d'appel attribue soit le retour à une propriété de type enveloppé (très probablement à la suite de quelque chose comme var mykeys = mycollection.keys; code>) ou utilise simplement la propriété directement dans une boucle "foreach". Notez que si l'énumérateur renvoyé par getenumerator () code> serait une structure, qui devra toujours être encadré dans tout cas. P>

L'avantage de la performance d'utilisation d'une structure plutôt que de la classe serait généralement assez légère; Cependant, l'utilisation d'une structure correspondrait à la recommandation générale que les propriétés ne créent pas de nouvelles instances d'objet de tas. Construire une nouvelle instance de struct qui ne contient que une référence à un objet de tas existant est très bon marché. Le plus gros inconvénient d'utiliser une structure telle que définie ici serait qu'il serait verrouillé dans le comportement de la chose renvoyée au code d'appel, alors que simplement renvoyer ienumerable code> permettrait d'autres approches. P >

Notez également que cela peut dans certains cas être possible d'éliminer l'exigence de tout boxe et d'exploiter les optimisations de typage de canard en C # et VB.net foreach code> en boucle si l'on utilise un type comme: P>

public struct FancyWrappedEnumerable<TItems,TEnumerator,TDataSource> : IEnumerable<TItems> where TEnumerator : IEnumerator<TItems>
{
    TDataSource _dataSource;
    Func<TDataSource,TEnumerator> _convertor;
    public FancyWrappedEnumerable(TDataSource dataSource, Func<TDataSource, TEnumerator> convertor)
    {
        _dataSource = dataSource;
        _convertor = convertor;
    }
    public TEnumerator GetEnumerator()
    {
        return _convertor(_dataSource);
    }
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return _convertor(_dataSource);
    }
}


0 commentaires