Frage

Dies ist eine Art schwer zu erklären, ich hoffe, mein Englisch reicht aus:

Ich habe eine Klasse "A", die eine Liste von Objekten der Klasse "B" (wie eine private List) halten sollte. Ein Verbraucher der Klasse „A“ sollte in der Lage sein, Elemente in die Liste hinzuzufügen. Nachdem die Artikel in die Liste aufgenommen werden, sollte der Verbraucher nicht in der Lage sein, sie wieder zu ändern, allein gelassen, dass er nicht in der Lage sein sollte, mit der Liste selbst (hinzufügen oder entfernen Artikel) zu temperieren. Aber er sollte die Elemente in der Liste aufzählen kann, und ihre Werte zu bekommen. Gibt es ein Muster für das? Wie würdest du das machen?

Wenn die Frage nicht klar genug ist, lassen Sie es mich wissen.

War es hilfreich?

Lösung

Bearbeitung der Liste oder seine Elemente, die Sie sie unveränderlich machen müssen, um zu verhindern, was bedeutet, Sie haben bei jeder Anfrage eine neue Instanz eines Elements zurück.

Siehe

Eric Lippert ausgezeichnete Reihe von "Unveränderlichkeit in C #": http://blogs.msdn.com/ericlippert/archive/tags/Immutability/C_2300_/default.aspx (Sie ein bisschen müssen nach unten scrollen)

Andere Tipps

Wow, gibt es einige übermäßig komplexe Antworten hier für ein einfaches Problem.

Haben Sie ein privates List<T>

Haben sie eine public void AddItem(T item) Methode - wenn Sie diese nicht mehr funktioniert entschließen, sie arbeiten zu stoppen. Sie könnten eine Ausnahme auslösen, oder man könnte es nur leise machen scheitern. Hängt davon ab, was Sie da drüben bekam los.

Haben Sie eine Methode, die public T[] GetItems() return _theList.ToArray() nicht

Wie viele dieser Antworten zeigen, gibt es viele Möglichkeiten, um die Sammlung selbst unveränderlich zu machen.

Es ist mehr Mühe nimmt, die Mitglieder der Sammlung unveränderlich zu halten. Eine Möglichkeit ist, eine Fassade / Proxy (sorry für die fehlende Kürze) zu verwenden:

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();
    }
}

Der Nachteil dieser Lösung ist, dass der Anrufer über eine Sammlung von ProxyB Objekte werden iteriert, anstatt die B-Objekte fügten sie hinzu.

EDIT: Zusätzliche Unterstützung für die Ausgabe Kontexte. Anrufer können nur Elemente innerhalb einer Ausgabe Kontext hinzufügen. Sie können Aditionally erzwingen, dass nur eine Ausgabe Kontext kann für die gesamte Lebensdauer der Instanz erstellt werden.


Mit Kapselung Sie eine beliebige Menge von Richtlinien definieren, können die inneren privaten Member zuzugreifen. Das folgende Beispiel ist eine grundlegende Umsetzung Ihrer Anforderungen:

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();
        }
    }
}

Sie möchten grundsätzlich zu vermeiden, Verweise auf die Klasse B-Punkte zu verschenken. Aus diesem Grund sollten Sie eine Kopie der Elemente tun.

ich denke, das kann mit der ToArray () -Methode eines List-Objekt gelöst werden. Sie brauchen eine tief Kopie der Liste zu erstellen, wenn Sie Änderungen geschützt werden sollen.

Generell gilt: die meisten der Zeit es nicht der Mühe wert ist, eine Kopie zu tun, gutes Verhalten zu erzwingen, vor allem, wenn Sie auch die Verbraucher schreiben

.
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>();
}

Der Nachteil ist, dass ein Verbraucher die Elemente ändern kann es von dem Enumerator wird, ist eine Lösung, deep der privaten Liste zu machen .

Es war nicht klar, ob Sie auch die B-Instanzen benötigten selbst unveränderlich zu sein, um einmal in die Liste. Sie können durch die Verwendung einer Nur-Lese-Schnittstelle für B einen Trick hier spielen, und nur diese durch die Liste ausgesetzt wird.

internal class B : IB
{
    private string someData;

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

public interface IB
{
    string SomeData { get; }
}

Die einfachste, was ich denken kann, ist eine Rückkehr Nur-Lese-Version der zugrunde liegenden Auflistung, wenn die Bearbeitung ist nicht mehr erlaubt.

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

Persönlich obwohl, ich würde die zugrunde liegende Liste an den Client nicht aussetzen und nur Methoden für das Hinzufügen, Entfernen und Aufzählen der B-Instanzen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top