대한 액세스를 제어하는 내부 컬렉션에서는 c#필요한 패턴
-
02-07-2019 - |
문제
이 종류의 하드를 설명하고,나는 영어를 충분:
내가 있는 클래스"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 인스턴스를 추가, 제거 및 열거하는 방법을 제공합니다.