我有将私有集合返回给调用者的方法,我想阻止调用者修改返回的集合。
private readonly Foo[] foos;
public IEnumerable<Foo> GetFoos()
{
return this.foos;
}
目前私有集合是一个固定数组,但如果需要在运行时添加新项目,集合可能会成为一个列表。
有几种解决方案可以防止调用者修改集合。返回IEnumerable<T>
是最简单的解决方案,但调用者仍然可以将返回值上传到IList<T>
并修改集合。
((IList<Foo>)GetFoos())[0] = otherFoo;
克隆集合的明显缺点是有两个集合可以独立发展。到目前为止,我已经考虑了以下选项。
ReadOnlyCollection<T>
。Enumerable
这样的虚拟投影,返回list.Select(item => item)
类定义的一个LINQ迭代器。实际上我考虑使用Where(item => true)
,因为返回的迭代器似乎更轻量级。我不喜欢使用ReadOnlyCollection<T>
,它实现IList<T>
并调用Add()
或访问索引器会导致异常。虽然这在理论上是绝对正确的,但几乎没有真正的代码检查IList<T>.IsReadOnly
或IList<T>.IsFixedSize
。
使用LINQ迭代器 - 我将代码包装在扩展方法MakeReadOnly()
中 - 阻止了这种情况,但它具有黑客的味道。
编写自定义包装器?重新发明轮子?
有任何想法,注意事项或其他解决方案吗?
在标记此问题时,我发现this Stack Overflow question我之前没有注意到。 Jon Skeet也建议使用“LINQ hack”,但使用Skip(0)
更有效。
答案 0 :(得分:5)
不幸的是,在当前版本的框架中无法实现完全您正在寻找的内容。它根本没有具体类型和接口样式的可索引不可变/只读集合的概念。
正如您所指出的那样,ReadOnlyCollection<T>
在具体类型方面运行正常。但是没有相应的接口可以实现,这也是静态只读的。
你真正的选择是......
IEnumerable<T>
,要么定义集合实现的需要只读接口。答案 1 :(得分:0)
如何制作要返回的对象的深层副本? [因此,即使来电者决定对所返回的副本进行更改,也不会对原始收藏产生任何影响]
答案 2 :(得分:0)
List和Array有一个AsReadOnly方法:
public IEnumerable<Foo> GetFoos()
{
return Array.AsReadOnly(this.foos);
// or if it is a List<T>
// return this.foos.AsReadOnly();
}
答案 3 :(得分:0)
在这种情况下,我在类中创建一个方法来访问私有集合的各个元素。 您还可以在包含私有集合的类上实现索引器(http://msdn.microsoft.com/en-us/library/6x16t2tx.aspx)。