为啥 System.Array 类实现 IList 但不提供 Add()
Posted
技术标签:
【中文标题】为啥 System.Array 类实现 IList 但不提供 Add()【英文标题】:Why System.Array class implements IList but does not provide Add()为什么 System.Array 类实现 IList 但不提供 Add() 【发布时间】:2016-11-02 05:45:45 【问题描述】:这段代码:
int[] myArr = 1, 2 ;
myArr.Add(3);
在构建时抛出以下错误:
错误 CS1061:“System.Array”不包含“Add”的定义,并且找不到接受“System.Array”类型的第一个参数的扩展方法“Add”(您是否缺少 using 指令或汇编参考?)
IList
接口有Add()
方法,但是为什么Array没有实现呢?
更新:我从答案中看到它确实明确地实现了它,好的,我明白了,谢谢,我最好坚持这个问题:
为什么Array
实际上没有提供 Add()
,或者,更好的是,为什么它必须首先实现IList
?除了实现IList
,它可以是另一个接口(例如IArray
),它可能只对IList
的数组成员有用-例如。 IsFixedSize
、IsReadOnly
、IndexOf()
...只是一个想法。
【问题讨论】:
如果你把它转换成IList
,你就可以调用Add
(类可以实现一些接口explicitly,这意味着你只能访问(全部)将其视为该接口时的接口方法)。不过你会失望的。数组有固定的大小。
那为什么语言不简单地从 Array 类签名中删除 :IList 呢?
它没有。 Array 实现了IList
- 但如果你想将它作为 IList
使用,你必须将它转换为该类型的变量。
@Damien_The_Unbeliever,抱歉我更正了这个问题,我的意思是为什么它没有删除它!
这能回答你的问题吗? Why array implements IList?
【参考方案1】:
为什么 Array 实际上不提供 Add()?
数组的大小是固定的,所以你不能添加新元素。
维度的数量和每个维度的长度是 在创建数组实例时建立。这些值不能 在实例的生命周期内发生变化。 https://msdn.microsoft.com/en-us/library/9b9dty7d.aspx
为什么必须首先实现 IList?
IList的定义:表示一个非泛型的对象集合 可以通过索引单独访问。
https://msdn.microsoft.com/en-us/library/system.collections.ilist.aspx
Array 是通过索引访问的,而 IList 容纳了这个索引,这就是 Array 实现 IList 的原因。
供参考:Why array implements IList?
【讨论】:
那为什么要实现IList(有Add)? @cnom 你可以在这里看到:***.com/questions/5968708/why-array-implements-ilist【参考方案2】:是的,如果System.Array
实现了IReadOnlyList
或类似的接口,它似乎应该是一个更好的设计。但是,IReadOnlyList<T>
出现在 .Net 4.5 中,而 System.Array
保留在最初的 .Net 1.0 中。微软,恕我直言,尽了最大的努力并隐藏 Add
通过显式接口实现
http://referencesource.microsoft.com/#mscorlib/system/array.cs,156e066ecc4ccedf
...
int IList.Add(Object value)
throw new NotSupportedException(Environment.GetResourceString("NotSupported_FixedSizeCollection"));
...
所以你不能这样做
int[] myArr = 1, 2 ;
myArr.Add(3);
但你可以坚持使用Add
(并得到NotSupportedException
)通过
((IList) myArr).Add(3);
甚至
if (!myArr.IsFixedSize)
// we have very strange array, let's try adding a value to it
((IList) myArr).Add(3);
【讨论】:
【参考方案3】:它确实提供了 Add,但是通过抛出 NotSupportedException
(参见 MSDN),因为数组的大小是固定的。
你得到一个编译错误的原因,相反,是因为接口是显式实现的,所以如果你想调用你需要转换为IList
的方法。请参阅有关 explicit interface implementation 的 C# 指南。
【讨论】:
【参考方案4】:虽然实现接口的类必须实现接口的所有成员,但它可以显式地实现它们:
public class MyList<T> : IList<T>
// ... shortened for simplicity
void ICollection<T>.Add(T item) // explicit implementation
如果您以这种方式实现该方法,它将在MyList<T>
的实例上不可见:
MyList<int> list = new MyList<int>();
list.Add(5); // would NOT compile
((IList<int>)list).Add(5); // can be compiled
因此,如果您有int[]
,您可以这样做:
int[] array = new int[0];
((IList<int>)array).Add(5);
它会编译,但在运行时会抛出NotSupportedException
,因为数组有固定大小,你不能添加一个新元素到数组,因为它的大小是在初始化时确定 (new int[0]
)。
【讨论】:
Add()
不是ICollection
的一部分
@haim770 是的:reference source
你是对的。我提到了非通用版本,它是由Array
(明确)实现的【参考方案5】:
System.Array 类实现 IList 但不提供 Add()
当然它是通过显式实现来实现的(没有办法实现接口而不实现某些成员)。
为什么数组实现
IList
?
嗯,主要是表明它支持索引器。
但实际上数组实现了有效的IList
用法之一。 IList
接口有一个名为IsFixedSize
的属性,根据文档
获取一个值,该值指示 IList 是否具有固定大小。
然后
固定大小的集合在集合创建后不允许添加或删除元素,但允许修改现有元素。
所以数组实现返回IsFixedSize = true
并在Add
、Insert
、Remove
和RemoveAt
方法中抛出NotSupportedException
。
【讨论】:
谢谢!这更接近我需要的答案。我还没有接受答案,因为我想知道为什么 IList 首先被放在那里!可能有另一个接口(例如 IArray),它可能只对 IList 的 Array 成员有用 - 例如IsFixedSize、IsReadOnly、IndexOf()- 你是对的。 AFAIC BCL 设计师考虑了这些替代方案并决定走这条路。通用IList<T>
也一样,它也有这样的缺点。从 .NET 4.5 开始,IReadOnlyList<T>
最接近数组是什么(但它缺少索引器设置器)。【参考方案6】:
根据msdn:
IList.Add(对象)
调用此方法总是会引发 NotSupportedException异常。
Array Class - 见显式接口实现部分
Array
有这个方法。要调用此方法,您应该显式转换为IList
。这个方法不能调用,因为数组是固定大小的,这个大小不能动态改变。
【讨论】:
【参考方案7】:IList
在三个不同的类别中实现:
显然Array
类型是IList
的固定大小实现。
无法从Array
访问Add()
方法的原因是该方法是显式实现的:
public class A : IList
public void IList.Add(object o)
...
这意味着您需要先将数组转换为 IList
,然后才能使用 Add
方法(即使它会引发不受支持的异常)。
你可能会说这是一个糟糕的设计,很多人会同意你的观点。
阅读更多关于明确定义的接口: https://msdn.microsoft.com/en-us/library/aa288461(v=vs.71).aspx
【讨论】:
以上是关于为啥 System.Array 类实现 IList 但不提供 Add()的主要内容,如果未能解决你的问题,请参考以下文章
为啥我使用此 WinRT 代码得到“'System.Array' 不包含 'AsBuffer' 的定义”?