Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
以c#模式控制对内部集合的访问需要_C#_Design Patterns_Collections_Data Access - Fatal编程技术网

以c#模式控制对内部集合的访问需要

以c#模式控制对内部集合的访问需要,c#,design-patterns,collections,data-access,C#,Design Patterns,Collections,Data Access,这很难解释,我希望我的英语足够: 我有一个类“a”,它应该维护一个类“B”的对象列表(类似于私有列表)。类别为“A”的消费者应该能够将项目添加到列表中。将项目添加到列表中后,消费者不应再次修改它们,更不用说他不应修改列表本身(添加或删除项目)。但是他应该能够列举列表中的项目并得到它们的值。有什么模式吗?你会怎么做 如果问题不够清楚,请告诉我。编辑:添加了对编辑上下文的支持。调用者只能在版本上下文中添加元素。您可以强制执行在实例的生存期内只能创建一个版本上下文 使用封装,您可以定义任何一组策略来

这很难解释,我希望我的英语足够:

我有一个类“a”,它应该维护一个类“B”的对象列表(类似于私有列表)。类别为“A”的消费者应该能够将项目添加到列表中。将项目添加到列表中后,消费者不应再次修改它们,更不用说他不应修改列表本身(添加或删除项目)。但是他应该能够列举列表中的项目并得到它们的值。有什么模式吗?你会怎么做


如果问题不够清楚,请告诉我。

编辑:添加了对编辑上下文的支持。调用者只能在版本上下文中添加元素。您可以强制执行在实例的生存期内只能创建一个版本上下文


使用封装,您可以定义任何一组策略来访问内部私有成员。以下示例是您的需求的基本实现:

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();
        }
    }
}
命名空间控制台应用程序2
{
使用制度;
使用System.Collections.Generic;
使用系统集合;
B类
{
}
接口可编辑
{
void StartEdit();
void StopEdit();
}
类EditContext:IDisposable,其中T:IEditable
{
私人T型父母;
公共编辑上下文(T父级)
{
parent.StartEdit();
this.parent=parent;
}
公共空间处置()
{
this.parent.StopEdit();
}
}
A类:IEnumerable,IEditable
{
私有列表_myList=新列表();
私有布尔可编辑;
公共空间添加(B o)
{
如果(!可编辑)
{
抛出新的NotSupportedException();
}
_添加(o);
}
公共EditContext ForEdition()
{
返回新的EditContext(此);
}
公共IEnumerator GetEnumerator()
{
返回_myList.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
返回此.GetEnumerator();
}
公开作废已启动IT()
{
this.editable=true;
}
公共void StopEdit()
{
this.editable=false;
}
}
班级计划
{
静态void Main(字符串[]参数)
{
A=新的A();
使用(EditContext edit=a.ForEdition())
{
a、 添加(新B());
a、 添加(新B());
}
foreach(B o in a)
{
WriteLine(o.GetType().ToString());
}
a、 添加(新B());
Console.ReadLine();
}
}
}

您基本上希望避免泄露对B类项目的引用。这就是为什么你应该做一个项目的副本

我认为这可以通过List对象的ToArray()方法解决。如果要防止更改,则需要创建列表的深度副本


一般来说:大多数情况下,为了实施良好行为而进行复制是不值得的,尤其是当您同时编写消费者时。

为了防止编辑列表或其项目,您必须进行复制,这意味着您必须在每次请求时返回元素的新实例

请参阅Eric Lippert的优秀系列“C#中的不变性”:(您必须向下滚动一点)

公共类MyList:IEnumerable{
公共MyList(IEnumerable源){
data.AddRange(源);
}
公共IEnumerator GetEnumerator(){
返回data.Enumerator();
}
私有列表数据=新列表();
}

缺点是消费者可以修改从枚举器获取的项目,解决方案是制作私有列表的deepcopy。

不清楚是否还需要B实例本身在添加到列表后保持不变。您可以在这里玩一个把戏,使用B的只读接口,并且只通过列表公开这些接口

internal class B : IB
{
    private string someData;

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

public interface IB
{
    string SomeData { get; }
}

我能想到的最简单的方法是,如果不再允许编辑,则返回基础集合的

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

但就我个人而言,我不会向客户机公开底层列表,而只是提供添加、删除和枚举B实例的方法。

哇,对于一个简单的问题,这里有一些过于复杂的答案

拥有私人
列表

有一个
public void AddItem(T item)
方法-每当你决定让它停止工作时,就让它停止工作。您可以抛出一个异常,也可以让它安静地失败。这取决于你在那边发生了什么


有一个
public T[]GetItems()
方法,该方法返回list.ToArray()

正如许多答案所示,有许多方法可以使集合本身不可变

保持集合的成员不变需要更多的努力。一种可能是使用facade/proxy(很抱歉,它不够简洁):

B类
{
公共B(内部数据)
{ 
这个数据=数据;
}
公共整数数据
{
获取{return privateData;}
设置{privateData=value;}
}
私有数据;
}
ProxyB类
{
公共代理(B)
{ 
实际=b;
}
公共整数数据
{
获取{return actual.data;}
}
私人B实际;
}
A类:IEnumerable
{
私有列表bList=新列表();
类代理枚举器:IEnumerator
{
私有IEnumerator b_enum;
公共代理枚举器(IEnumerator benum)
{
b_enum=benum;
}
公共图书馆
{
返回b_enum.MoveNext();
}
公共代理电流
{
获取{returnnewproxyb(b_enum.Current);}
}
对象IEnumerator。
public IList ListOfB
{
    get 
    {
        if (_readOnlyMode) 
            return listOfB.AsReadOnly(); // also use ArrayList.ReadOnly(listOfB);
        else
            return listOfB;
    }
}
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();
    }
}