Question

J'ai un ICollection<T> appelé foos dans ma classe que je souhaite exposer en lecture seule (voir cette question ). Je vois que l'interface définit une propriété .IsReadOnly, qui semble appropriée ... Ma question est la suivante: comment rendre évident pour le consommateur de la classe que .Add() est en lecture seule?

Je ne veux pas compter sur eux pour se souvenir d'interroger ReadOnlyCollection<T> avant d'essayer une méthode non implémentée telle que IList<T>. Idéalement, j'aimerais exposer foo en tant que GetReadOnlyFooCollection, mais il ne met pas en œuvre ToList(). Devrais-je exposer <=> via une méthode appelée, par exemple <=> plutôt que via une propriété? Si tel est le cas, cela ne risque-t-il pas de confondre quelqu'un qui attend un <=>?

Il s’agit de C # 2.0, les méthodes d’extension telles que <=> ne sont pas disponibles ...

Était-ce utile?

La solution

Je semble avoir décidé de renvoyer IEnumerable avec les objets clonés.

public IEnumerable<Foose> GetFooseList() {
   foreach(var foos in Collection) {
     yield return foos.Clone();
   }
}
  • nécessite une méthode Clone sur Foos.

Ceci n'autorise aucun changement dans la collection. N'oubliez pas que ReadonlyCollection est & Quot; leaky & Quot; puisque les objets qu'il contient peuvent être modifiés comme mentionné dans un lien dans un autre message.

Autres conseils

Vous pouvez créer & "foos &"; un ReadOnlyCollection comme ceci:

ReadOnlyCollection<T> readOnlyCollection = foos.ToList<T>().AsReadOnly();

Ensuite, vous pouvez l'exposer comme une propriété de votre classe.

EDIT:

    class FooContainer
    {
        private ICollection<Foo> foos;
        public ReadOnlyCollection<Foo> ReadOnlyFoos { get { return foos.ToList<Foo>().AsReadOnly();} }

    }

Remarque: rappelez-vous qu'une fois que vous avez obtenu la collection ReadOnlyFoos, elle n'est plus & "synchronisée &"; avec vos foos ICollection. Voir le fil que vous avez référencé .

Depuis que la question a été écrite, .NET 4.0 a ajouté une interface IReadOnlyCollection<T>; il serait probablement bon de l'utiliser comme type de retour déclaré.

Cela laisse toutefois ouverte la question du type d'instance à retourner . Une approche serait de cloner tous les éléments de la collection d'origine. Un autre serait de toujours retourner un wrapper en lecture seule. Une troisième solution consisterait à renvoyer la collection d'origine si elle implémente List<T>. Chaque approche sera la meilleure dans certains contextes, mais sera moins qu'idéale (ou peut-être même terrible) dans d'autres. Malheureusement, Microsoft ne fournit pas de moyen standard permettant de poser une question à deux questions très importantes:

  1. Vous promettez-vous de toujours et à jamais contenir les mêmes objets que vous le faites actuellement?

  2. Pouvez-vous être exposé en toute sécurité directement à du code qui n'est pas censé modifier votre contenu.

Le style d’emballage approprié dépend de ce que le code client attend de ce qu’il reçoit. Quelques scénarios à éviter:

  1. Un objet a été fourni d'un type que le client reconnaîtrait comme immuable, mais au lieu d'être renvoyé directement, il est dupliqué, en utilisant un type que le client ne reconnaît pas comme immuable. Par conséquent, le client est obligé de dupliquer à nouveau la collection.

  2. Un objet a été fourni d'un type que le client reconnaîtrait comme immuable, mais avant d'être renvoyé, il est emballé de manière à ce que le client ne puisse pas dire si la collection est immuable ou non, et est donc obligé de dupliquer.

  3. Un objet de type mutable qui n'est pas supposé être muté est fourni par un client (converti en une interface en lecture seule). Il est ensuite exposé directement à un autre client, qui détermine qu'il s'agit d'un type mutable et le modifie.

  4. Une référence à une collection mutable est reçue et est encapsulée dans un wrapper en lecture seule avant d'être renvoyée à un client ayant besoin d'un objet immuable. La méthode qui a rendu la collection a promis qu’elle était immuable, et le client a donc refusé de faire sa propre copie défensive. Le client est alors mal préparé à la possibilité que la collection change.

Il n’existe pas vraiment de " safe " ligne de conduite qu'un objet peut entreprendre avec les collections qu'il reçoit de certains clients et qu'il doit exposer à d'autres. Dans de nombreuses circonstances, le plus sûr est de toujours dupliquer le plus possible, mais cela peut facilement conduire à des situations dans lesquelles une collection qui n'aurait pas besoin d'être dupliquée finit par être dupliquée des centaines ou des milliers de fois. Renvoyer les références telles qu'elles ont été reçues est souvent l'approche la plus efficace, mais elle peut aussi être dangereuse sur le plan sémantique.

J'aimerais que Microsoft ajoute un moyen standard de poser les questions ci-dessus aux collections ou, mieux encore, de produire un instantané immuable de leur état actuel. Une collection immuable peut renvoyer un instantané immuable de son état actuel à très bon marché (il suffit de le renvoyer lui-même) et même certains types de collection mutables peuvent renvoyer un instantané immuable à un coût bien inférieur au coût d'un dénombrement complet (par exemple, un T[][] peut être sauvegardé par deux ICloneable tableaux, dont l'un contient des références à des tableaux immuables partageables de 256 éléments et l'autre, à des tableaux inaltérables mutables de 256 éléments. Pour créer un instantané d'une liste, il suffit de cloner les tableaux intérieurs contenant des éléments. qui ont été modifiées depuis le dernier instantané - potentiellement beaucoup moins cher que le clonage de la liste complète. Malheureusement, il n’existe pas de norme & "; créer un instantané immuable &"; interface [notez que <=> ne compte pas , puisqu’un clone d’une liste mutable serait mutable, alors que l’on pourrait créer un instantané immuable en encapsulant un clone mutabledans un wrapper en lecture seule, cela ne fonctionnerait que pour les choses clonables, et même les types non clonables devraient quand même supporter un & "instantané mutable &"; fonction.]

Ma recommandation est de retourner utiliser un ReadOnlyCollection < T > pour le scénario directement. Cela rend l'utilisation explicite pour l'utilisateur appelant.

Normalement, je suggérerais d'utiliser l'interface appropriée. Mais étant donné que le .NET Framework ne dispose pas actuellement d’un IReadOnlyCollection approprié, vous devez utiliser le type ReadOnlyCollection.

Vous devez également savoir que vous utilisez ReadOnlyCollection, car il n’est pas en lecture seule: Immutabilité et ReadOnlyCollection

Parfois, vous pouvez utiliser une interface, peut-être parce que vous souhaitez simuler la collection lors des tests unitaires. Veuillez consulter mon entrée de blog pour ajouter votre propre interface à ReadonlyCollection à l'aide de un adaptateur.

Je retourne généralement un IEnumerable < T > .

Une fois que vous avez créé une collection en lecture seule (des méthodes telles que Ajouter , Supprimer et Effacer ne fonctionnent plus), il ne reste plus grand-chose qu'une collection prend en charge le fait qu’il n’est pas possible d’énumérer - juste Count et contient , je crois.

Si les consommateurs de votre classe ont vraiment besoin de traiter les éléments comme s'ils faisaient partie d'une collection, il est assez facile de passer un IEnumerable au List < T > . .

Renvoyer un T []:

private ICollection<T> items;

public T[] Items
{
    get { return new List<T>(items).ToArray(); }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top