为什么数组实现IList?


140

请参阅System.Array类的定义

public abstract class Array : IList, ...

从理论上讲,我应该能够写点东西并开心

int[] list = new int[] {};
IList iList = (IList)list;

我也应该能够从iList调用任何方法

 ilist.Add(1); //exception here

我的问题不是为什么我会得到异常,而是为什么Array实现了IList


21
问题。我从不喜欢胖接口的想法(这是这种设计的技术术语)。
康拉德·鲁道夫


2
有人真的在乎LSP吗?对我来说似乎很学术。
加布

13
@Gabe,那么您需要使用更大的代码库。实施行为(从接口继承),然后简单地忽略您不喜欢/不支持的内容,会导致产生臭味,模糊不清的内容,并最终导致错误的代码。
Marius 2012年

3
@Gabe表示隐含可变性的集合,而不是其包含的实体。您可以使您的类成员同时实现IRWList <>和IReadList <>,并在类内部将if用作IRWList <>并将其公开为IReadList。是的,您必须将复杂性放在某处,但我只是看不到如何将LSP视为非常好的设计原则(不知道IsReadOnly属性,尽管从消费者的角度来看,这会使IList变得更加复杂)
Marius

Answers:


93

因为数组允许按索引快速访问,所以IList/ IList<T>是唯一支持此功能的集合接口。因此,也许您真正的问题是:“为什么没有索引器的常量集合的接口?” 对此,我没有答案。

也没有用于集合的只读接口。我想念那些甚至超过带有索引器接口的恒定大小的东西。

IMO应当根据集合的功能,再有几个(通用)集合接口。而且名称也应该有所不同,List因为带有索引器的东西确实是愚蠢的IMO。

  • 只是枚举 IEnumerable<T>
  • 只读但没有索引器(.Count,.contains等)
  • 可调整大小,但没有索引器,即设置为(添加,删除等)当前 ICollection<T>
  • 索引器(indexer,indexof,...)只读
  • 带有索引器的恒定大小(带有设置器的索引器)
  • 带有分度器(Insert,...)电流的可变大小 IList<T>

我认为当前的收集接口设计不好。但是,由于它们具有告诉您哪些方法有效的属性(这是这些方法的约定的一部分),因此不会违反替代原理。


14
感谢你的回答。但我宁愿将问题保留原样。原因很简单。接口是公共合同。如果实现了它,则必须完全实现所有成员,否则将破坏LSP并通常发出难闻的气味,不是吗?
oleksii 2011年

16
它确实破坏了LSP。如果未列出,则无论具体类型如何,Add(item)都应将项目添加到列表中。例外情况除外。在数组实现中,在非exceptionel情况下会引发异常,这本身就是不好的做法
符文FS

2
@闻到很抱歉,但是您的LSP错误。数组没有实现add,因此不能代替需要该功能的事情。
符文FS

7
我承认,从技术上讲,它不违反LSP 仅仅是因为文档指出您应该检查IsFixedSizeIsReadOnly属性,它绝对违反了“告诉,不要问”原则“最不惊奇的原则。当您只打算在9种方法中的4种抛出异常时,为什么要实现一个接口?
马修

11
自最初的问题以来已经过去了一段时间。但现在有了.Net 4.5,还有其他接口IReadOnlyListIReadOnlyCollection
Tobias'Apr

42

备注一节的文档IList

IList是ICollection接口的后代,并且是所有非泛型列表的基本接口。 IList实现分为三类:只读,固定大小和可变大小。只读的IList无法修改。固定大小的IList不允许添加或删除元素,但允许修改现有元素。可变大小的IList允许添加,删除和修改元素。

显然,数组属于固定大小类别,因此通过定义接口是有意义的。


4
我想他们最终会拥有很多接口。IListFixedSize,IListReadOnly ...
Magnus

9
从文档的角度来看,这实际上是一个很好的答案。但是对我来说,它看起来像是一个hack。为了使一个类实现所有成员,接口必须是简单的。
oleksii 2011年

1
@oleksii:我同意。接口和运行时异常不是最完美的组合。为此,Array它确实Add明确实现了该方法,从而降低了意外调用该方法的风险。
布莱恩·拉斯穆森

在我们创建该实现之前IList,不允许修改添加/删除。然后,文档不再正确。:P
Timo

1
@Magnus-在.Net 4.5中,还有其他接口IReadOnlyListIReadOnlyCollection
RBT

17

因为并非所有ILists都是可变的(请参阅IList.IsFixedSizeIList.IsReadOnly),所以数组的行为肯定类似于固定大小的列表。

如果您的问题确实是“为什么要实现一个非泛型接口”,那么答案是这些问题早在泛型出现之前就已存在。


10
@oleksii:不,它不会破坏LSP,因为接口IList 本身告诉您它可能是不可变的。如果实际上保证它是可变的,而数组告诉您否则,那么它将违反规则。
user541686

其实,阵列无休息LSP在一般情况下,IList<T>在非一般情况下,不会分开IListenterprisecraftsmanship.com/2014/11/22/...
弗拉基米尔·

5

从不清楚如何处理只读集合以及Array是否为只读的时代开始,这就是我们的遗产。IList接口中有IsFixedSize和IsReadOnly标志。IsReadOnly标志意味着完全不能更改集合,而IsFixedSize意味着该集合允许修改,但不允许添加或删除项。

在.NET 4.5的时候,很明显,一些“中间”的接口都需要工作,只读集合,因此IReadOnlyCollection<T>IReadOnlyList<T>进行了介绍。

这是一篇很棒的博客文章,描述了详细信息:.NET中的只读集合


0

IList接口的定义是“表示可以由索引单独访问的对象的非通用集合”。数组完全满足此定义,因此必须实现接口。调用Add()方法时发生异常“ System.NotSupportedException:集合的大小是固定的”,并且由于数组无法动态增加其容量而发生。其容量是在创建数组对象期间定义的。


0

使用数组实现IList(以及可传递的ICollection)简化了Linq2Objects引擎,因为将IEnumerable强制转换为IList / ICollection也适用于数组。

例如,由于Count()强制转换为ICollection并且数组的实现返回Length,因此最终调用了Array.Length进行了后台处理。

没有这个,Linq2Objects引擎将不会对数组进行特殊处理并表现出可怕的效果,或者它们需要加倍代码,为数组添加特殊情况处理(就像对IList所做的那样)。他们必须选择使数组实现IList。

这就是我对“为什么”的看法。


0

还有实现细节LINQ Last检查IList,如果它没有实现list,他们将需要2次检查来减慢所有Last调用的速度,或者在使用O(N)的数组上使用Last

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.