ICollection<T>.IsReadOnly的合约

8
我正在编写一个实现 IList<T> 接口的数组包装类,但我不确定要为 IList<T>.IsReadOnly(从 ICollection<T> 继承)返回什么值。
我的类禁止插入和删除。它允许通过 this[int].set 属性修改项目。 MSDN 中指出:

只读集合在创建后不允许添加、删除或修改元素。

对于我的类来说,这似乎意味着我必须返回 true,但在我看来,这使得该属性完全无用:据我所知,使用此方法的方式如下:
客户端处理任意 IList 并需要将元素插入其中,如果可能的话。他们可以通过调用 Insert 并捕获生成的 NotSupportedException 来完成此操作 - 出于各种原因,这可能是不可取的。因此,客户端可以事先测试 IsReadOnly 属性,而不是引发异常。
但是,该属性的结果将是 错误的,因为它将集合的可修改性与其内容的可修改性混淆在一起 - 这些是完全无关的问题
当然,有 IList.IsFixedSize 属性,但这是一个单独的类型(IList<T> 不扩展 IList)。我该怎么办?还要实现 IList 吗(我真的不喜欢这个选择)?做些其他的事情吗?

1
只是补充一下,使用ReadOnlyCollection<T>时,如果内容可修改(如果可变),仍然可以进行修改,但“IsReadOnly”属性返回true... - bruno conde
@bruno - 这取决于您所说的“编辑内容”是否指替换给定位置上的项目(list[n] = item),以及变异给定位置上的项目(list[n].SomeProp = value)。 - Marc Gravell
3个回答

2
我认为,要符合所定义的合同,您需要返回 true
您可以(另外)实现 IBindingList - 这个接口有 AllowNewAllowEditAllowRemove。您需要从 AllowEdit 返回 true,而从其他两个中返回 false
不过,是否检查了这一点取决于调用者。不过,很多UI绑定代码都会检查。
补充:
此外,如果您正在实现 IList<T>,则您也应该实现 IList;特别是,在一些反射和绑定场景中,类型事先是未知的,因此 IList 非常重要。

谢谢 - 我(再一次)完全忘记了绑定(我很少使用它)。我会遵循你的建议。 - Konrad Rudolph

2

还有一些需要考虑的问题...

你的集合是一个数组包装器,具有一些类似于数组的语义。例如,不能插入或删除项,但可以修改它们。

数组在IsReadOnly返回false,而IsFixedSize返回true

我认为除了实现IList<T>之外,还应该实现IList,并模仿数组的行为,就像IsReadOnlyIsFixedSize一样。

MSDN中的说明中关键词是“或”:

一个只读的集合不允许在创建集合后添加、删除或修改元素。

你的集合允许修改,因此在我的看法中,如果返回true表示IsReadOnly会违反这个约定。


重要提示:如果您在数组上调用公共的IsReadOnly属性,则会返回false。该公共属性也是非泛型接口IListIsReadOnly的实现。如果该数组是一个“向量”,即它是一维的并且从索引0开始,就像“普通”数组一样,那么该数组还实现了泛型接口IList<>。然后,泛型接口IList<>ICollection<>继承了IsReadOnly。这个ICollection<>.IsReadOnly是通过一种“神奇”的显式接口实现来实现的。此实现返回true! - Jeppe Stig Nielsen
1
一个例子:int[] arr = { 2, 4, 6, }; bool x = arr.IsReadOnly; /* false */ bool y = ((IList<int>)arr).IsReadOnly; /* true! */ 因此,如果提问者想要像数组一样行事,他应该为_generic_接口的实现返回true - Jeppe Stig Nielsen

1
在这里,修改的语义很重要。修改集合元素和修改集合中包含的对象之间存在区别。想象一下集合中实际空间的元素。您不能添加空间、删除空间或更改特定空间中的对象。这就是“IsReadOnly”遵守的契约。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接