为什么System.Array类实现了IList接口但没有提供Add()方法?

11

这段代码:

int[] myArr = { 1, 2 };
myArr.Add(3);

在构建时会抛出以下错误:

错误 CS1061:'System.Array'不包含 'Add' 的定义,并且没有接受类型为 'System.Array' 的第一个参数的扩展方法 'Add' 可用(您是否缺少 using 指令或程序集引用?)

IList 接口有 Add() 方法,但是为什么 Array 没有实现它呢?

更新:从答案中我看到它确实明确地实现了,好的,我明白了,谢谢,我应该更好地坚持问题:

为什么 Array 实际上没有提供 Add(),或者更好的说法是,为什么它必须首先实现 IList?它可以是另一个接口(例如 IArray),该接口只能包含对于 Array 有用的 IList 成员 - 例如 IsFixedSizeIsReadOnlyIndexOf()... 这只是一个想法。


2
如果您将其转换为 IList,那么您将能够调用 Add(类可以显式实现一些接口,这意味着当将其作为该接口处理时,您仅获得访问该接口的所有方法)。然而,你会失望的。数组具有固定的大小。 - Damien_The_Unbeliever
1
那么为什么语言不直接从Array类签名中删除:IList呢? - cnom
它并不是。数组实现了IList接口 - 但如果你想将其用作IList,你必须将其强制转换为该类型的变量。 - Damien_The_Unbeliever
@Damien_The_Unbeliever,抱歉我更正了问题,我的意思是为什么它没有将其删除! - cnom
1
这个回答解决了你的问题吗?为什么数组实现IList接口? - Michael Freidgeim
7个回答

12

为什么数组不提供 Add() 方法?

数组的大小是固定的,因此您无法添加新的元素。

  

数组实例创建时确定维度数和每个维度的长度。这些值在实例的生命周期中不能改变。   https://msdn.microsoft.com/en-us/library/9b9dty7d.aspx

那么,为什么它还要首先实现 IList 呢?

  

IList 的定义是:表示可以通过索引单独访问的对象的非泛型集合。

  

https://msdn.microsoft.com/en-us/library/system.collections.ilist.aspx

由于数组是通过索引访问的,而 IList 可以容纳这个索引,所以数组实现了 IList。

参考:为什么数组实现了 IList 接口?


那么为什么它要实现IList(其中包含Add方法)呢? - cnom
1
@cnom,您可以在这里查看:https://dev59.com/7W025IYBdhLWcg3wf2KE - currarpickt

9
是的,如果System.Array实现了IReadOnlyList或类似的接口,则看起来应该是更好的设计。但是,IReadOnlyList出现在.Net 4.5中,而System.Array始终停留在最初的.Net 1.0。据我所知,Microsoft已经尽力通过显式接口实现隐藏了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)via

((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类型。请参阅C#指南中关于显式接口实现的内容。


3
尽管实现接口的类必须实现接口的所有成员,但它们可以显式地实现这些成员:
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])。


1
Add() 不是 ICollection 的一部分。 - haim770
@haim770 是的,它是:参考来源 - René Vogt
1
你说得对。我指的是非泛型版本,这个版本被“Array”在问题中明确地实现了。 - haim770

2
System.Array 类实现了 IList 接口,但是没有提供 Add() 方法。
当然,它通过显式实现来提供该方法(没有办法实现接口却不实现一些成员)。
为什么数组要实现 IList 接口呢?
主要是为了表明它支持索引器。
但事实上,数组实现了 IList 接口的一个有效用法。IList 接口有一个名为 IsFixedSize 的属性,根据文档,它表示:
获取一个值,该值指示 IList 是否具有固定大小。
然后,一个具有固定大小的集合在创建之后不允许添加或删除元素,但允许修改现有元素。
因此,数组实现返回 IsFixedSize = true,并在 Add、Insert、Remove 和 RemoveAt 方法中抛出 NotSupportedException 异常。

谢谢!这更接近我需要的答案了。我还没有接受答案,因为我想知道为什么一开始要放置IList!可能会有另一个接口(例如IArray),它只包含IList中有用的数组成员 - 例如IsFixedSize,IsReadOnly,IndexOf()。 - cnom
1
你是对的。就我所知,BCL 设计者们考虑了这些替代方案并决定采取这条路线。泛型 IList<T> 也有类似的缺点。从 .NET 4.5 开始,IReadOnlyList<T> 最接近数组(但它缺少索引器设置器)。 - Ivan Stoev

1

根据 msdn:

IList.Add(Object)

调用此方法将始终引发NotSupportedException异常。

Array Class - 请参见显式接口实现部分

Array 有这个方法。要调用此方法,您应该明确转换为 IList。由于数组具有固定大小,因此无法动态更改此大小,因此无法调用此方法。


1

IList有三种不同的实现方式:

  • 只读
  • 可变大小
  • 固定大小

显然,Array类型是IList的一种固定大小的实现。 你不能从一个Array访问Add()方法的原因是该方法被显式地实现了:

public class A : IList {
    public void IList.Add(object o){
         ...
    }
}

这意味着在使用Add方法之前,您需要将数组转换为IList(即使它会抛出不支持的异常)。
你可能会说这是一个糟糕的设计,许多人也会同意这一点。
了解更多关于显式定义接口的信息: https://msdn.microsoft.com/en-us/library/aa288461(v=vs.71).aspx

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