3

我有将私有集合返回给调用者的方法,我想阻止调用者修改返回的集合。

private readonly Foo[] foos;

public IEnumerable<Foo> GetFoos()
{
    return this.foos;
}

目前私有集合是一个固定数组,但将来如果需要在运行时添加新项目,该集合可能会变成一个列表。

有几种解决方案可以防止调用者修改集合。返回IEnumerable<T>是最简单的解决方案,但调用者仍然可以向上转换返回值IList<T>并修改集合。

((IList<Foo>)GetFoos())[0] = otherFoo;

克隆集合具有明显的缺点,即有两个集合可以独立进化。到目前为止,我已经考虑了以下选项。

  1. 将集合包装在ReadOnlyCollection<T>.
  2. 通过Enumerable执行虚拟投影(如list.Select(item => item). 实际上我考虑使用Where(item => true)因为返回的迭代器看起来更轻量级。
  3. 编写自定义包装器。

我不喜欢使用ReadOnlyCollection<T>的是它实现IList<T>和调用Add()或访问索引器会导致异常。虽然这在理论上是绝对正确的,但几乎没有真正的代码检查IList<T>.IsReadOnlyIList<T>.IsFixedSize.

使用 LINQ 迭代器 - 我将代码包装在扩展方法中MakeReadOnly()- 可以防止这种情况,但它有一种 hack 的味道。

编写自定义包装器?重新发明轮子?

有什么想法、考虑或其他解决方案吗?


在标记这个问题时,我发现了这个我以前没有注意到的Stack Overflow 问题。Jon Skeet 也建议使用“LINQ hack”,但使用Skip(0).

4

4 回答 4

5

不幸的是,没有办法在当前版本的框架中准确地实现您正在寻找的东西。它根本没有关于具体类型和接口样式的可索引不可变/只读集合的​​概念。

正如您所指出的,ReadOnlyCollection<T>在具体类型方面工作正常。但是没有相应的接口来实现,它也是静态只读的。

你唯一真正的选择是...

  • 定义自己的集合类
  • 仅实现IEnumerable<T>或定义您的集合实现的需要只读接口。
于 2009-08-02T17:04:02.610 回答
0

制作被返回对象的深层副本怎么样?[所以即使调用者决定对返回的副本进行更改也不会对原始集合产生影响]

于 2009-08-02T18:03:08.243 回答
0

List 和 Array 有一个 AsReadOnly 方法:

public IEnumerable<Foo> GetFoos()
{
    return Array.AsReadOnly(this.foos);
    // or if it is a List<T>
    // return this.foos.AsReadOnly();
}
于 2009-08-02T18:10:48.690 回答
0

在这种情况下,我在类中创建一个方法来访问私有集合的各个元素。您还可以在包含私有集合的类上实现索引器 ( http://msdn.microsoft.com/en-us/library/6x16t2tx.aspx )。

于 2009-08-02T20:00:03.030 回答