Array.IsReadOnly不一致,具体取决于接口实现

时间:2009-11-19 18:24:44

标签: .net c#-3.0 .net-3.5

类型化数组实现System.Collections.IListSystem.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。

4 个答案:

答案 0 :(得分:6)

From MSDN

  

IList是该家族的后裔   ICollection接口是基础   所有非通用列表的接口。   IList实现分为三个   类别:只读,固定大小和   大小可变的。只读IList   无法修改。固定大小的IList   不允许添加或删除   元素,但它允许   修改现有元素。一个   可变大小的IList允许   添加,删除和修改   元件。

ICollection&lt; T&gt;接口没有索引器,因此固定大小的ICollection&lt; T&gt;是自动只读 - 无法修改现有项目。

可能ICollection&lt; T&gt; .IsFixedSize将是一个比ICollection&lt; T&gt; .IsReadOnly更好的属性名称,但两者都意味着相同的东西 - 无法添加或删除元素,即与IList.IsFixedSize相同。

数组是固定大小的列表,但由于元素可以修改,因此不是只读的。

作为ICollection&lt; T&gt;,它是只读的,因为ICollection&lt; T&gt;无法修改元素。

这可能看起来令人困惑,但它是一致和合乎逻辑的。

稍微不一致的是通用IList&lt; T&gt;。 interface具有从ICollection&lt; T&gt;继承的IsReadOnly属性。因此,其语义与非通用的IList.IsReadOnly不同。我想设计师已经意识到这种不一致性,但出于向后兼容的原因,我们无法返回并更改非通用IList的语义。

总而言之,IList可以是:

  • 可变大小。

    IList.IsFixedSize = false

    IList.IsReadOnly = false

    ICollection&lt; T&gt; .IsReadOnly = false

  • 固定大小(但可以修改元素,例如数组)

    IList.IsFixedSize = true

    IList.IsReadOnly = false

    ICollection&lt; T&gt; .IsReadOnly = true

  • 只读(无法修改元素)

    IList.IsFixedSize = true

    IList.IsReadOnly = true

    ICollection&lt; T&gt; .IsReadOnly = true

答案 1 :(得分:4)

这一决定充满了痛苦,正如对feedback article的评论中所表明的那样。

答案 2 :(得分:0)

此行为的原因归结为具有2个IsReadOnly属性的System.Array

第一个是类型数组的普通属性。此属性满足IList接口的IsReadOnly属性。无论出于什么原因,在CLR的1.0中,他们认为该属性应该返回true。

第二个是类型ICollection<T>的显式属性实现(实际上由CLR IIRC实现)。在这种情况下,IsReadOnly返回true,因为类型Array无法满足ICollection<T>的变异方法,例如Add,Clear等...

真正的问题是为什么版本之间的变化?我实际上并不知道,但我的猜测是作者确定将Array视为只读时更合适,因为它被视为一个单独的集合。虽然它可以满足部分可变方法,但它无法满足所有这些方法。因此,将它视为只读和可变是更安全的。

答案 3 :(得分:0)

来自Array Class的文档:

  

在.NET Framework 2.0版中,Array类实现了   System.Collections.Generic.IList<T>,   System.Collections.Generic.ICollection<T>,和   System.Collections.Generic.IEnumerable<T>通用接口。该   实现被提供给数组   在运行时,因此不是   对文档构建可见   工具。结果,通用   接口不会出现在   Array的声明语法   上课,并没有参考   接口成员的主题   只能通过将数组转换为   通用接口类型(显式   接口实现)。 关键   当你施展时要注意的事情   数组到这些接口之一是   添加,插入或的成员   删除元素抛出   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。