在 C# 中控制对内部集合的访问 - 需要模式
这有点难以解释,我希望我的英语足够:
我有一个类“A”,它应该维护类“B”的对象列表(如私有列表)。 “A”类消费者应该能够将项目添加到列表中。 将项目添加到列表后,消费者不应该能够再次修改它们,更不用说他不应该能够修改列表本身(添加或删除项目)。 但他应该能够枚举列表中的项目并获取它们的值。 有一个模式吗? 你会怎么做?
如果问题不够清楚,请告诉我。
This is kind of hard to explain, I hope my English is sufficient:
I have a class "A" which should maintain a list of objects of class "B" (like a private List). A consumer of class "A" should be able to add items to the list. After the items are added to the list, the consumer should not be able to modify them again, left alone that he should not be able to temper with the list itself (add or remove items). But he should be able to enumerate the items in the list and get their values. Is there a pattern for it? How would you do that?
If the question is not clear enough, please let me know.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
为了防止编辑列表或其项目,您必须使它们不可变,这意味着您必须在每次请求时返回元素的新实例。
请参阅 Eric Lippert 的优秀系列“C# 中的不变性”:http: //blogs.msdn.com/ericlippert/archive/tags/Immutability/C_2300_/default.aspx(您必须向下滚动一点)
To prevent editing the list or its items you have to make them immutable, which means you have to return a new instance of an element on every request.
See Eric Lippert's excellent series of "Immutability in C#": http://blogs.msdn.com/ericlippert/archive/tags/Immutability/C_2300_/default.aspx (you have to scroll down a bit)
正如许多答案所示,有很多方法可以使集合本身不可变。
需要付出更多努力来保持集合成员的不可变性。 一种可能性是使用外观/代理(抱歉不够简洁):
此解决方案的缺点是调用者将迭代 ProxyB 对象的集合,而不是他们添加的 B 对象。
As many of these answers show, there are many ways to make the collection itself immutable.
It takes more effort to keep the members of the collection immutable. One possibility is to use a facade/proxy (sorry for the lack of brevity):
The downside of this solution is that the caller will be iterating over a collection of ProxyB objects, rather than the B objects they added.
目前尚不清楚您是否还需要 B 实例本身在添加到列表后保持不变。 您可以在这里玩点小把戏,为 B 使用只读接口,并仅通过列表公开这些接口。
It wasn't clear whether you also needed the B instances themselves to be immutable once added to the list. You can play a trick here by using a read-only interface for B, and only exposing these through the list.
编辑:添加了对版本上下文的支持。 调用者只能在版本上下文中添加元素。 您还可以强制要求在实例的生命周期内只能创建一个版本上下文。
使用封装,您可以定义任何策略集来访问内部私有成员。 以下示例是您需求的基本实现:
EDIT: Added support for edition contexts. Caller can only add elements inside an edition context. You can aditionally enforce that only one edition context can be created for the lifetime of the instance.
Using encapsulation you can define any set of policies to access the inner private member. The following example is a basic implementation of your requirements:
您基本上希望避免泄露对 B 类项目的引用。 这就是为什么您应该复制这些项目。
我认为这可以通过 List 对象的 ToArray() 方法来解决。 如果您想防止更改,则需要创建列表的深层副本。
一般来说:大多数时候,不值得通过复制来强制执行良好行为,尤其是当您还编写消费者时。
You basically want to avoid to give away references to the class B items. That's why you should do a copy of the items.
I think this can be solved with the ToArray() method of a List object. You need to create a deep-copy of the list if you want to prevent changes.
Generally speaking: most of the times it is not worthwhile to do a copy to enforce good behaviour, especially when you also write the consumer.
缺点是消费者可以修改从枚举器获取的项目,解决方案是对私有 List进行深度复制。
The downside is that a consumer can modify the items it gets from the Enumerator, a solution is to make deepcopy of the private List<T>.
我能想到的最简单的方法是返回 只读版本如果不再允许编辑,则为基础集合。
但就我个人而言,我不会向客户端公开底层列表,而只是提供添加、删除和枚举 B 实例的方法。
The simplest that I can think of is return a readonly version of the underlying collection if editing is no longer allowed.
Personally though, I would not expose the underlying list to the client and just provide methods for adding, removing, and enumerating the B instances.
哇,对于一个简单的问题,这里有一些过于复杂的答案。
拥有一个私有
List
拥有一个
public void AddItem(T item)
方法 - 每当您决定让它停止工作时,就让它停止工作。 您可以抛出异常,也可以让它默默地失败。 取决于你在那里发生了什么。有一个
public T[] GetItems()
方法,返回 _theList.ToArray()
Wow, there are some overly complex answers here for a simple problem.
Have a private
List<T>
Have an
public void AddItem(T item)
method - whenever you decide to make that stop working, make it stop working. You could throw an exception or you could just make it fail silently. Depends on what you got going on over there.Have a
public T[] GetItems()
method that doesreturn _theList.ToArray()