Array.IsReadOnly 不一致,取决于接口实现
Posted
技术标签:
【中文标题】Array.IsReadOnly 不一致,取决于接口实现【英文标题】:Array.IsReadOnly inconsistent depending on interface implementation 【发布时间】:2010-12-18 10:33:01 【问题描述】:类型化数组实现了System.Collections.IList
和System.Collections.Generic.ICollection<T>
接口,它们都有自己的IsReadOnly
属性。但是这里到底发生了什么?
var array = new int[10];
Console.WriteLine(array.IsReadOnly); // prints "False"
var list = (System.Collections.IList)array;
Console.WriteLine(list.IsReadOnly); // prints "False"
var collection = (System.Collections.Generic.ICollection<int>)array;
Console.WriteLine(collection.IsReadOnly); // prints "True"
数组的IList
视图的行为与我预期的一样,返回的结果与数组本身相同,但是数组的ICollection<T>
视图返回true。
对这种行为有任何合理的解释,还是编译器/CLR 错误? (如果是后者,我真的会很惊讶,因为你想象这会在现在之前被发现,但它太违反直觉了,我想不出解释会是什么......)。
我正在使用 C#3.0/.NET 3.5 SP1。
【问题讨论】:
有趣的是,两者的注释都说“只读的集合不允许在集合创建后添加、删除或修改元素。”。而且我认为逻辑等同于 IsReadOnly = !CanAdd || !可以删除 || !CanEdit 应该返回 true,因为您可以替换列表指定索引处的值。 一个但更多,但 Array.IsReadonly 文档是明确的..“对于所有数组,此属性始终为 false。” “Array 实现 IsReadOnly 属性,因为 System.Collections..::.IList 接口需要它。如果需要只读集合,请使用实现 System.Collections..::.IList 的 System.Collections 类接口。只读数组不允许在数组创建后添加、删除或修改元素。" 【参考方案1】:这种行为的原因归结为 System.Array
具有 2 个 IsReadOnly 属性
第一个是类型数组上的普通属性。此属性满足 IList 接口的 IsReadOnly 属性。无论出于何种原因,在 CLR 1.0 中,他们都认为该属性应该返回 true。
第二个是 ICollection<T>
类型的显式属性实现(实际上由 CLR IIRC 实现)。在这种情况下,IsReadOnly 返回 true,因为类型 Array
不能满足 ICollection<T>
的变异方法,例如 Add、Clear 等...
真正的问题是为什么版本之间会发生变化?我实际上不知道,但我的猜测是作者确定将Array
视为只读时更合适,因为它被视为单独的集合。虽然它可以满足部分可变方法,但它不能满足所有可变方法。因此,将其视为只读与可变更安全。
【讨论】:
ICollection<T>.IsReadOnly
因为不能满足ICollection<T>
接口的所有方法而返回true的解释并不真正成立,因为IList.IsReadOnly
返回false并且不能满足所有方法IList
接口。所以我仍然不知道这是否是一个错误,或者如果不是这个决定背后的理由是什么。
@Greg,我相信这代表了 CLR v1 和 v2 之间思想的转变。可能是由于客户投诉,尽管我找不到任何证据。【参考方案2】:
来自Array Class 的文档:
在 .NET Framework 2.0 版中,Array 类实现了
System.Collections.Generic.IList<T>
,System.Collections.Generic.ICollection<T>
,和System.Collections.Generic.IEnumerable<T>
通用接口。这 提供给数组的实现 在运行时,因此不是 对文档构建可见 工具。结果,通用 接口不会出现在 数组的声明语法 类,并且没有参考 接口成员的主题是 只能通过将数组转换为 通用接口类型(显式 接口实现)。 关键 施放时要注意的事情 这些接口之一的数组是 添加、插入或 删除元素抛出NotSupportedException
.
因此,由于通用集合不支持添加、插入或删除,IsReadOnly
返回 true。
至于为什么System.Collections.IList
不返回false?我的猜测是
var array = new int[10];
Console.WriteLine(array.IsReadOnly); // prints "True"
array[0] = 5; // WTF? This is readonly.
不是他们想看到的。当他们在 v2 中添加泛型接口时,这些只能在数组被强制转换时调用,因此对于那些返回 true 更有意义。
【讨论】:
这实际上并没有回答这个问题; IsReadOnly 属性为什么会为底层数组返回不同的值是没有道理的。【参考方案3】:From MSDN:
IList 是 ICollection 接口是基础 所有非通用列表的接口。 IList 实现分为三种 类别:只读、固定大小和 可变大小。一个只读的 IList 无法修改。固定大小的 IList 不允许添加或删除 的元素,但它允许 修改现有元素。一种 可变大小的 IList 允许 添加、删除和修改 元素。
ICollection
可能 ICollection
数组是一个固定大小的列表,但不是只读的,因为元素可以修改。
作为 ICollection
这可能看起来令人困惑,但它是一致且合乎逻辑的。
有点不一致的是,泛型 IList
总而言之,IList 可以是:
可变大小。
IList.IsFixedSize = false
IList.IsReadOnly = 假
ICollection
固定大小(但可以修改元素,例如数组)
IList.IsFixedSize = true
IList.IsReadOnly = 假
ICollection
只读(不能修改元素)
IList.IsFixedSize = true
IList.IsReadOnly = true
ICollection
【讨论】:
【参考方案4】:对于这个决定有很多痛苦,这在 feedback article 上的 cmets 中很明显。
【讨论】:
看起来这给出了内部推理(这似乎是“我们知道它是垃圾,但在发布周期中做任何事情都为时已晚”)。所以这是设计使然,但不是一个好的设计。 反馈文章的链接似乎已损坏。还有其他链接吗?以上是关于Array.IsReadOnly 不一致,取决于接口实现的主要内容,如果未能解决你的问题,请参考以下文章