2009-08-02 76 views
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. 通过执行像list.Select(item => item)这样的虚拟投影来返回Enumerable类定义的LINQ迭代器之一。其实我考虑使用Where(item => true),因为返回的迭代器看起来更轻量级。
  3. 编写自定义包装。

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

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

写一个自定义包装?重新发明轮子?

任何想法,考虑或其他解决方案?


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

+1

除非存在安全问题(潜在的敌对调用者),否则我通常不担心防止不明智转换或调用ReadOnlyCollection上的Add。愚蠢的开发人员也可以使用反射和访问内部。代码防白痴是很困难的。目标是防止无辜使用公共接口。 – TrueWill 2010-01-22 20:56:40

回答

5

不幸的是,没有办法实现刚好你在当前版本的框架中寻找什么。它对于具体类型和界面风格都没有可索引的不可变/只读集合的​​概念。

正如您所指出的那样,ReadOnlyCollection<T>可以在混凝土型面上正常工作。但是没有相应的接口来实现,它也是静态只读的。

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

  • 定义自己的集合类
  • 要么只能实现IEnumerable<T>或定义一个需要,你的集合实现只读接口。
+0

我接受了这个答案,因为它表明没有内置支持。决定坚持Enumerable.Skip(0)。 – 2009-08-18 14:52:37

+0

@DanielBrückner:与`ReadOnlyCollection `包装和铸造`IEnumerable `相比,有什么真正的优势呢?事实上,生成的对象可以转换为`IList `将极大地提高一些Linq方法(如Count和Last)的性能。 – supercat 2012-11-27 21:37:45

0

如何使返回的对象的深层副本? [于是就有即使呼叫方决定返回给更改复制的原始集合没有影响]

+0

集合的副本不可取,因为调用者只会获取快照,并不会注意到对集合所做的更改。 – 2009-08-02 23:06:43

0

List和Array有AsReadOnly方法:

public IEnumerable<Foo> GetFoos() 
{ 
    return Array.AsReadOnly(this.foos); 
    // or if it is a List<T> 
    // return this.foos.AsReadOnly(); 
} 
+0

这个方法返回包裹在一个ReadOnlyCollection 收集和我不喜欢在问题描述这个类实现IList 。 – 2009-08-02 23:09:16

相关问题