Question

C'est un peu difficile à expliquer, j'espère que mon anglais sera suffisant:

J'ai une classe & "A &"; ce qui devrait maintenir une liste d'objets de classe " B " (comme une liste privée). Un consommateur de classe & Quot; A & Quot; devrait pouvoir ajouter des éléments à la liste. Une fois les éléments ajoutés à la liste, le consommateur ne devrait plus être en mesure de les modifier, mais il ne devrait pas pouvoir manipuler la liste elle-même (ajouter ou supprimer des éléments). Mais il devrait pouvoir énumérer les éléments de la liste et obtenir leurs valeurs. Y at-il un modèle pour cela? Comment feriez-vous cela?

Si la question n'est pas suffisamment claire, merci de me le faire savoir.

Était-ce utile?

La solution

Pour empêcher la modification de la liste ou de ses éléments, vous devez les rendre immuable , ce qui signifie vous devez renvoyer une nouvelle instance d'un élément à chaque demande.

Voir l'excellente série de & "Immutabilité en C #" de Eric Lippert: http://blogs.msdn.com/ericlippert/archive/tags/Immutability/C_2300_/default.aspx (vous devez faire défiler un peu)

Autres conseils

Wow, il existe des réponses trop complexes ici pour un problème simple.

Avoir un privé List<T>

Utilisez une public void AddItem(T item) méthode - chaque fois que vous décidez de faire cesser ce travail, faites-le cesser de fonctionner. Vous pouvez lancer une exception ou simplement le faire échouer en silence. Cela dépend de ce que vous avez là-bas.

Avoir une public T[] GetItems() méthode qui fait return _theList.ToArray()

Comme le montrent bon nombre de ces réponses, il existe de nombreuses façons de rendre la collection elle-même immuable.

Il faut plus d’efforts pour que les membres de la collection restent immuables. Une possibilité consiste à utiliser une façade / un proxy (désolé pour le manque de brièveté):

class B
{
    public B(int data) 
    { 
        this.data = data; 
    }

    public int data
    {
        get { return privateData; }
        set { privateData = value; }
    }

    private int privateData;
}

class ProxyB
{
    public ProxyB(B b)   
    { 
        actual = b; 
    }

    public int data
    {
        get { return actual.data; }
    }

    private B actual;
}

class A : IEnumerable<ProxyB>
{
    private List<B> bList = new List<B>();

    class ProxyEnumerator : IEnumerator<ProxyB>
    {
        private IEnumerator<B> b_enum;

        public ProxyEnumerator(IEnumerator<B> benum)
        {
            b_enum = benum;
        }

        public bool MoveNext()
        {
            return b_enum.MoveNext();
        }

        public ProxyB Current
        {
            get { return new ProxyB(b_enum.Current); }
        }

        Object IEnumerator.Current
        {
            get { return this.Current; }
        }

        public void Reset()
        {
            b_enum.Reset();
        }

        public void Dispose()
        {
            b_enum.Dispose();
        }
    }

    public void AddB(B b) { bList.Add(b); }

    public IEnumerator<ProxyB> GetEnumerator()
    {
        return new ProxyEnumerator(bList.GetEnumerator());
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

L’inconvénient de cette solution est que l’appelant itérera sur une collection d’objets ProxyB, plutôt que sur les objets B qu’ils ont ajoutés.

EDIT: Ajout de la prise en charge des contextes d'édition. L'appelant peut uniquement ajouter des éléments dans un contexte d'édition. Vous pouvez également appliquer qu'un seul contexte d'édition peut être créé pour la durée de vie de l'instance.

À l'aide de l'encapsulation, vous pouvez définir un ensemble de règles pour accéder au membre privé interne. L'exemple suivant est une implémentation de base de vos exigences:

namespace ConsoleApplication2
{
    using System;
    using System.Collections.Generic;
    using System.Collections;

    class B
    {
    }

    interface IEditable
    {
        void StartEdit();
        void StopEdit();
    }

    class EditContext<T> : IDisposable where T : IEditable
    {
        private T parent;

        public EditContext(T parent)
        {
            parent.StartEdit();
            this.parent = parent;
        }

        public void Dispose()
        {
            this.parent.StopEdit();
        }
    }

    class A : IEnumerable<B>, IEditable
    {
        private List<B> _myList = new List<B>();
        private bool editable;

        public void Add(B o)
        {
            if (!editable)
            {
                throw new NotSupportedException();
            }
            _myList.Add(o);
        }

        public EditContext<A> ForEdition()
        {
            return new EditContext<A>(this);
        }

        public IEnumerator<B> GetEnumerator()
        {
            return _myList.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        public void StartEdit()
        {
            this.editable = true;
        }

        public void StopEdit()
        {
            this.editable = false;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            using (EditContext<A> edit = a.ForEdition())
            {
                a.Add(new B());
                a.Add(new B());
            }

            foreach (B o in a)
            {
                Console.WriteLine(o.GetType().ToString());
            }

            a.Add(new B());

            Console.ReadLine();
        }
    }
}

Vous voulez essentiellement éviter de donner des références aux éléments de classe B. C’est pourquoi vous devriez en faire une copie.

Je pense que cela peut être résolu avec la méthode ToArray () d'un objet List. Vous devez créer une copie complète de la liste si vous souhaitez empêcher les modifications.

De manière générale: il n'est généralement pas utile de faire une copie pour imposer un bon comportement, en particulier lorsque vous écrivez également le consommateur.

public class MyList<T> : IEnumerable<T>{

    public MyList(IEnumerable<T> source){
        data.AddRange(source);
    }

    public IEnumerator<T> GetEnumerator(){
        return data.Enumerator();
    }

    private List<T> data = new List<T>();
}

L’inconvénient est qu’un consommateur peut modifier les éléments qu’il obtient de l’énumérateur. Une solution consiste à copier en profondeur la liste privée < T >.

Il n'était pas clair si vous aviez également besoin que les instances B elles-mêmes soient immuables une fois ajoutées à la liste. Vous pouvez jouer un tour ici en utilisant une interface en lecture seule pour B et en ne les exposant que dans la liste.

internal class B : IB
{
    private string someData;

    public string SomeData
    {
        get { return someData; }
        set { someData = value; }
    }
}

public interface IB
{
    string SomeData { get; }
}

Le plus simple auquel je puisse penser est de renvoyer une version en lecture seule de la collection sous-jacente si la modification n'est plus autorisée.

public IList ListOfB
{
    get 
    {
        if (_readOnlyMode) 
            return listOfB.AsReadOnly(); // also use ArrayList.ReadOnly(listOfB);
        else
            return listOfB;
    }
}

Personnellement, je n’exposerais pas la liste sous-jacente au client et fournirais simplement des méthodes pour ajouter, supprimer et énumérer les instances B.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top