使用List<T>相对于IEnumerable<T>有什么好处?

9

还是反过来呢?

我经常使用通用列表。但是我偶尔也会听到关于IEnumerables的说法,而且今天我真的不知道它们是用来做什么的,以及为什么我应该使用它们。因此,冒着在网络上永远标榜我的无知的风险,我谦卑地发布这个问题。


不是的。我非常确定我是指List<T>。 - Byron Sommardahl
8个回答

16

嗯,List<T> 实现了 IEnumerable<T> 接口...基本上,IEnumerable<T> 就是一系列项目的序列。你可以逐个阅读它们,这就是全部内容。

List<T> 是一个可变集合 - 你可以添加到它里面、从它中删除、对其进行排序等。它本身更加灵活,但是 IEnumerable<T> 允许你使用相同的代码来处理任何实现(如数组、链表、列表、使用 yield 语句返回的迭代器等)。


由于 IEnumerable<T> 是不可变的,因此您可以在从类返回数据时使用此接口,因为知道不会添加或删除任何元素。尽管用户仍然可以对可枚举对象中包含的对象调用突变函数。 - Marcel Gosselin
3
@Marcel:并不完全正确。IEnumerable<T>接口不包含任何改变序列的方法,具体的实现可以是可变的,也可能是不可变的。如果一个方法签名声明返回 IEnumerable<Whatever>,但实际上返回了一个 List<Whatever>,那么调用代码可以轻易地将返回的对象强制转换成List<Whatever>并修改列表内容。但是该方法签名确实 传达了结果应被当作只读来处理 - Fredrik Mörk
1
在调用站点仍然可以将其转换回IList<T>,因此这并不是百分之百可靠的。 - recursive
1
一个回转到IList<T>的开发者应该被吊销他们的许可证! - Phil
3
@Phil: 在很多情况下,将IEnumerable<T>转换为IList<T>是合适的。例如,.Last扩展方法在可能的情况下似乎会将其参数转换为IList<T>;因此,可以在不必阅读前9999999个项目的情况下几乎即时地检索10000000个项目列表的最后一个项目。 - supercat

6

IEnumerable<T>是一个更通用的接口,因此您可以为您的操作替换任何实现该接口的内容。如果您有这个方法:

public void DoSomething(IEnumerable<T> enumerable) {
}

它可以运用于数组、集合、列表、字典以及任何实现了该接口的对象。
如果您指定对象为List<T>,该方法只能作用于List<T>对象或继承自它的实例。
使用List<T>的优点是,列表比可枚举对象拥有更多功能。当您需要这些功能(插入、搜索、转换等等)时,List<T>IList<T>更加适合。

6
约翰·斯基特(John Skeet)等人已经提供了有关List相对于IEnumerable的功能性的良好概述,所以我想填补另一半问题的空白,即“使用IEnumerable相比List有哪些好处?”

首先,IEnumerable提供更通用的合同来表示可以迭代的事物集合,因此允许您遵守最小特权原则。换句话说,在只需要公开枚举行为的任务中,应优先使用IEnumerable而不是其派生类型。这是有益的,因为不必要地公开信息和/或行为会使您的API面临可能的意外用途,这可能会导致安全问题或API与其消费者之间的意外耦合。

其次,IEnumerable是枚举的抽象,而List是该抽象的一种实现。遵循可重用面向对象软件元素的设计模式的指导,编写抽象而不是实现有助于使应用程序更具弹性,因为它允许稍后更改实现而不影响使用代码。如果确实需要列表行为,则应直接公开IList而不是List。


非常好的回答。你有点晚了,但我完全同意。自去年11月以来,我学到了很多东西。:) - Byron Sommardahl
我想为了后人的缘故发表一下我的看法 :) - Derek Greer

2
List<T> 相对于 IEnumerable<T> 的最大优势如下:
  1. 随机访问
  2. 计数属性
  3. ForEach 方法
  4. 可变性
前两个优点在 IEnumerable<T> 上也很容易实现,但无法保证 O(1) 速度。在计数方面,由于 IEnumerable<T> 可以轻松表示无限列表,因此甚至无法保证真实答案。

1
List提供了比IEnumerable更多的方法。您无法使用IEnumerable添加、插入或删除,但可以使用List。
当您仅计划循环浏览数据时,应使用IEnumerable。它比IList更具优势,因为您不必一次加载所有数据以通过访问数据,只需能够通过枚举器获取下一个记录即可。IEnumerable也是一个接口,因此您可以“隐藏”包含数据列表、数组等实际对象的类型。

0

如果你只需要在 IEnumerable<T> 中暴露和实现的功能,那么你应该选择它。另一种选择是 IEnumerable<T> where T : IFoo,如果你想要能够迭代实现接口 IFoo 的对象。然而,如果你需要像 List<T> 所暴露的 Count 等属性,那么你应该选择它。找到最低公共分母并选择它,因为这使得你的方法更加通用且易于使用。


0

IEnumerable 具有干净的只读和“可枚举”或“可查询”的语义。而使用 List,似乎允许任何人修改它:)。


0

IEnumerable的一个优点还没有被提到:逆变性。一个期望IEnumerable(Of Car)的程序会非常高兴地接受IEnumerable(Of HondaCivic),这意味着它也会接受IList(Of HondaCivic)。相比之下,一个期望IList(Of Car)的程序将不会满足于IList(Of HondaCivic)。如果Microsoft IList是从只读IReadableByIndex接口派生而来的,那么期望IReadableByIndex(Of Car)的代码将非常高兴地接受IReadableByIndex(Of HondaCivic),但这已经是过去时了。


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