C# 最佳实践:如何公开只读ICollection

C# 最佳实践:如何公开只读ICollection,c#,collections,c#-2.0,C#,Collections,C# 2.0,我的类中有一个名为foos的ICollection,我想以只读形式公开(请参阅)。我看到接口定义了一个属性.IsReadOnly,这似乎是合适的。。。我的问题是:如何让类的消费者清楚地知道foos是只读的 我不想依靠他们在尝试未实现的方法(如.Add())之前记住查询.IsReadOnly)。理想情况下,我希望将foos公开为ReadOnlyCollection,但它不实现IList。我是否应该通过一个名为GetReadOnlyFooCollection的方法而不是通过属性公开foo?如果是这样

我的类中有一个名为
foos
ICollection
,我想以只读形式公开(请参阅)。我看到接口定义了一个属性
.IsReadOnly
,这似乎是合适的。。。我的问题是:如何让类的消费者清楚地知道
foos
是只读的

我不想依靠他们在尝试未实现的方法(如
.Add()
)之前记住查询
.IsReadOnly
)。理想情况下,我希望将
foos
公开为
ReadOnlyCollection
,但它不实现
IList
。我是否应该通过一个名为
GetReadOnlyFooCollection
的方法而不是通过属性公开
foo
?如果是这样的话,这会不会让那些期待
ReadOnlyCollection
的人感到困惑

这是C#2.0,因此像
ToList()
这样的扩展方法不可用…

您可以使“foos”成为如下的只读集合:

ReadOnlyCollection<T> readOnlyCollection = foos.ToList<T>().AsReadOnly();
ReadOnlyCollection ReadOnlyCollection=foos.ToList().AsReadOnly();
然后可以将其作为类的属性公开

编辑:

class容器
{
私人ICollection foos;
public ReadOnlyCollection ReadOnlyFoos{get{return foos.ToList().AsReadOnly();}
}

注意:您应该记住,一旦获得ReadOnlyFoos集合,它就不再与您的foos ICollection“同步”。请参阅。

我的建议是返回并直接为场景使用ReadOnlyCollection。这使得使用对调用用户是明确的

通常我会建议使用适当的接口。但是,鉴于.NET Framework当前没有合适的IReadOnlyCollection,您必须使用ReadOnlyCollection类型

另外,在使用ReadOnlyCollection时必须注意,因为它实际上不是只读的:

返回一个T[]:

private ICollection<T> items;

public T[] Items
{
    get { return new List<T>(items).ToArray(); }
}
私有i收集项目;
公共T[]项目
{
获取{返回新列表(项).ToArray();}
}

有时您可能希望使用接口,可能是因为您希望在单元测试期间模拟集合。请参阅my,了解如何使用适配器将自己的接口添加到ReadonlyCollection。

我通常返回一个
IEnumerable

一旦您将集合设置为只读(因此像
Add
Remove
Clear
这样的方法不再有效),集合支持的可枚举项就不多了——我相信,只需
计数
包含


如果类的使用者真的需要像对待集合中的元素一样对待元素,那么将
IEnumerable
传递给
List
的构造函数就很容易了。

我似乎已经决定在克隆对象的情况下返回IEnumerable

public IEnumerable<Foose> GetFooseList() {
   foreach(var foos in Collection) {
     yield return foos.Clone();
   }
}
public IEnumerable GetFooseList(){
foreach(集合中的var foos){
产生返回foos.Clone();
}
}
  • 在Foos上需要克隆方法

这不允许在集合中进行任何更改。请记住,ReadonlyCollection是“泄漏的”,因为它内部的对象可以按照另一篇文章中的链接所述进行更改。

自从问题被写出来后,.NET 4.0添加了一个
IReadOnlyCollection
接口;最好使用它作为声明的返回类型

然而,这就留下了一个悬而未决的问题,即返回什么类型的实例。一种方法是克隆原始集合中的所有项目。另一种方法是始终返回只读包装。第三种方法是,如果实现了
IReadOnlyCollection
,则返回原始集合。在某些情况下,每种方法都是最好的,但在其他情况下则不太理想(或者完全可怕)。不幸的是,Microsoft没有提供标准的方法来提问两个非常重要的问题:

  • 你是否承诺永远包含与你现在一样的物品

  • 您是否可以安全地直接接触到不应该修改您的内容的代码

  • 哪种包装方式是合适的将取决于客户机代码希望对它接收的东西做什么。应避免的一些情况:

  • 提供的对象的类型是客户端可以识别为不可变的,但它不是直接返回,而是使用客户端无法识别为不可变的类型进行复制。因此,客户不得不再次复制该集合

  • 提供的对象的类型是客户机可以识别为不可变的,但在返回之前,该对象的包装方式使客户机无法判断集合是否不可变,因此被迫复制

  • 客户机提供了一个不应该被改变的可变类型的对象(转换为只读接口)。然后,它直接暴露给另一个客户机,该客户机确定它是可变类型并继续修改它

  • 接收对可变集合的引用,并将其封装在只读包装器中,然后返回给需要不可变对象的客户端。返回集合的方法承诺集合是不可变的,因此客户端拒绝创建自己的防御副本。然后,客户对集合可能发生变化的可能性准备不足

  • 对于对象从某些客户机接收到的集合,并需要向其他客户机公开的集合,实际上没有任何特别“安全”的操作过程。在许多情况下,总是复制所有内容是最安全的做法,但它很容易导致本不需要复制的集合最终被复制数百次或数千次。将引用作为recei返回
    public IEnumerable<Foose> GetFooseList() {
       foreach(var foos in Collection) {
         yield return foos.Clone();
       }
    }