문제

이 종류의 하드를 설명하고,나는 영어를 충분:

내가 있는 클래스"A"를 유지해야 합 list 객체의 클래스의"B"(개인 List).소비자 클래스의"A"할 수 있어야 합니에 항목을 추가 목록에 있습니다.후 품목 목록에 추가되어,소비자해야하지 않을 수정할 수 있는 그들을 다시 혼자 남아있는 그 수 없는 성질을 가진 목록에(항목을 추가하거나 제거).그러나 그는 할 수 있어야를 열거하고 항목 목록에서 자신의 값이 있습니다.패턴이 있습니다?어떻게 할 것인가 하는 것이?

질문이있는 경우,충분히 명확하지 않 알려주시기 바랍니다.

도움이 되었습니까?

해결책

을 방지하는 편집하는 목록 또는 그것의 항목을 있는 그들을 변경할 수 없, 즉,당신을 반환해야의 새 인스턴스 요소에 모든 요청을 합니다.

보 에릭은 르네상스 양식의 훌륭한 시리즈의"불변성에서는 C#": http://blogs.msdn.com/ericlippert/archive/tags/Immutability/C_2300_/default.aspx (당신은 아래로 스크롤을 조금)

다른 팁

와우, 간단한 문제에 대한 지나치게 복잡한 답변이 있습니다.

개인이 있습니다 List<T>

가지고있다 public void AddItem(T item) 방법 - 작동을 멈추기로 결정할 때마다 작동을 멈추게하십시오. 예외를 던질 수 있거나 조용히 실패 할 수 있습니다. 거기에서 무슨 일이 일어나고 있는지에 달려 있습니다.

가지고있다 public T[] GetItems() 하는 방법 return _theList.ToArray()

이 답변 중 다수가 알 수 있듯이 컬렉션 자체를 불변으로 만드는 방법에는 여러 가지가 있습니다.

컬렉션의 구성원을 불변으로 유지하려면 더 많은 노력이 필요합니다. 한 가지 가능성은 외관/프록시를 사용하는 것입니다 (간결함이 부족한 죄송합니다) :

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

이 솔루션의 단점은 발신자가 추가 된 B 객체보다는 프록시브 객체 모음을 반복한다는 것입니다.

편집하다: 에디션 컨텍스트에 대한 지원이 추가되었습니다. 발신자는 에디션 컨텍스트 내에 요소 만 추가 할 수 있습니다. 인스턴스의 수명에 대해 하나의 에디션 컨텍스트 만 만들 수 있음을 대중적으로 시행 할 수 있습니다.


캡슐화를 사용하면 내부 개인 구성원에게 액세스하기 위해 모든 정책 세트를 정의 할 수 있습니다. 다음 예는 요구 사항의 기본 구현입니다.

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

당신은 기본적으로 클래스 B 항목에 대한 언급을주지 않기를 원합니다. 그렇기 때문에 항목 사본을해야합니다.

나는 이것이 목록 개체의 ToArray () 메소드로 해결 될 수 있다고 생각합니다. 변경을 방지하려면 목록의 깊은 코피를 만들어야합니다.

일반적으로 말하면 : 대부분의 시간은 특히 소비자를 쓸 때 좋은 행동을 시행하기 위해 사본을 수행하는 것이 가치가 없습니다.

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

단점은 소비자가 열거 자에서 얻은 항목을 수정할 수 있다는 것입니다. 솔루션은 개인 목록의 심층을 만드는 것입니다.u003CT> .

일단 목록에 추가 된 B 인스턴스 자체가 불변이 필요하지 않은지는 확실하지 않았습니다. B에 대한 읽기 전용 인터페이스를 사용하여 목록을 통해서만 노출하여 여기서 트릭을 재생할 수 있습니다.

internal class B : IB
{
    private string someData;

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

public interface IB
{
    string SomeData { get; }
}

내가 생각할 수있는 가장 간단한 것은 반환입니다. Readonly 버전 편집이 더 이상 허용되지 않는 경우 기본 컬렉션의.

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

그러나 개인적으로, 나는 기본 목록을 클라이언트에 노출시키지 않으며 B 인스턴스를 추가, 제거 및 열거하는 방법을 제공합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top