如何防止方法调用者修改返回的集合?

时间:2009-08-02 17:00:19

标签: c# collections ienumerable readonly-collection

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

private readonly Foo[] foos;

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

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

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

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

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

  1. ReadOnlyCollection<T>
  2. 中包装该集合
  3. 通过执行像Enumerable这样的虚拟投影,返回list.Select(item => item)类定义的一个LINQ迭代器。实际上我考虑使用Where(item => true),因为返回的迭代器似乎更轻量级。
  4. 编写自定义包装器。
  5. 我不喜欢使用ReadOnlyCollection<T>,它实现IList<T>并调用Add()或访问索引器会导致异常。虽然这在理论上是绝对正确的,但几乎没有真正的代码检查IList<T>.IsReadOnlyIList<T>.IsFixedSize

    使用LINQ迭代器 - 我将代码包装在扩展方法MakeReadOnly()中 - 阻止了这种情况,但它具有黑客的味道。

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

    有任何想法,注意事项或其他解决方案吗?


    在标记此问题时,我发现this Stack Overflow question我之前没有注意到。 Jon Skeet也建议使用“LINQ hack”,但使用Skip(0)更有效。

4 个答案:

答案 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)。