IEnumerable和Array,IList和List有什么区别?


91

IEnumerable和之间有什么区别Array

IList和之间有什么区别List

这些似乎具有相同的功能。

Answers:


101

IEnumerable仅提供最少的“可迭代”功能。您可以遍历序列,仅此而已。这有缺点-例如,使用IEnumerable计数元素或获取第n个元素效率很低-但它也有优点-例如,IEnumerable可以是无穷序列,例如质数序列。

数组是一个固定大小的集合,具有随机访问权限(即,您可以对其进行索引)。

List是一个具有随机访问权限的可变大小的集合(即,您可以添加和删除元素)。

IList是一个接口,它从各种具体类(例如List,BindingList,ObservableCollection等)中抽象出列表功能(计数,添加,删除,索引器访问)。


2
根据此处接受的答案stackoverflow.com/questions/1826658/…。依靠IEnumerable几乎是相同的,因为它试图调用Count on ICollection
Karsten

15
是的,没有。Count()扩展方法检查IEnumerable是否也是ICollection,如果是,则调用其Count属性。如果IEnumerable 不是 ICollection,则Count()会回到迭代序列。也许我应该说“ 使用IEnumerable 来计数元素是非常低效的”,因为如果对象有一个IEnumerable,那么IEnumerable的存在并不会阻止您使用更有效的方法,而LINQ to Objects扩展方法确实可以那。
itowlson 2011年

16

IEnumerable是一个接口,它允许通过一组项进行迭代(例如,通过foreach关键字)。

数组是.NET固有的。它容纳相同类型的项目,但是大小固定。创建具有x个元素的数组后,它就无法增长或收缩。

IList定义列表的接口,并实现IEnumerable。

List实现IList接口;这是列表的具体类型。

.NET列表和数组之间的区别在于,列表可以添加元素-它们增长到足以容纳所有必需项的大小。列表将其内部存储在一个数组中,当该数组的大小不再足以容纳所有元素时,将创建一个新的数组并复制项目。

IList和数组都实现IEnumerable。接口就是这样工作的-类实现契约并以相似的方式运行,因此可以类似地被对待(您知道该类实现IEnumerable,您不需要知道操作方式或原因)。我建议您阅读接口等内容。


1
为没有阅读所有答案的读者添加此注释:数组和List <>都实现IList <>(数组显式实现了它的很多原因,大概是因为许多成员抛出NotSupportedException)。说IList继承自IEnumerable也比说实现它更正确。
phoog 2011年

9

IEnumerable和IList是 接口。数组和列表是类。数组实现IEnumerable。List实现了扩展IEnumerable的IList。

编辑:正如itowlson在评论中提到的,Array还实现了IList。


我也没有!但是,如果开始在数组上调用IList.RemoveAt会发生什么?
Mark Simpson

它抛出一个NotSupportedException。(请注意IList.RemoveAt和类似的IList方法是在Array上显式实现的,因此它们不会出现在常规Array引用上!)
itowlson 2009年

1
@itowlson我要指出的是,只有System.Array(本身是一个类)确实实现了IList,但是T []没有实现IList <T>。T []实现IEnumerable <T>。我觉得OP询问“阵”恕我直言,如果没有一定要求的System.Array
USR-本地ΕΨΗΕΛΩΝ

6

IEnumerable集合的生成很懒。例:

public IEnumerable<int> GetTwoInts()
{
  yield return 1;
  yield return 2;
}
public void Something()
{
  var twoInts = GetTwoInts();
}

在方法Something中,对GetTwoInts()的调用实际上不会导致方法GetTwoInts的执行,因为永远不会重复枚举。


您能详细说明一下吗?我不确定我是否理解正确。您能否稍后显示一行代码,实际上将导致该方法被执行?谢谢!
paaone

@paaone twoInts输入a foreach或call twoInts.ToList()
2013年

2

IEnumerable是由许多类,如使用的通用接口ArrayList并且String为了让别人迭代一个集合。基本上,这就是驱动foreach语句的原因。

IList通常是如何List向最终用户公开类型的变量。该接口允许随机访问基础集合。


2

为了补充其他答案,请注意,在执行foreach语句之间和执行foreach语句时,存在性能差异IList<T>List<T>

这是因为by List<T>.GetEnumerator所返回的迭代器对象是一个值类型,而by IList<T>.GetEnumerator所返回的对象是引用类型,因此需要进行内存分配(请参阅c#中list的枚举类型的枚举器)。

我认为,IList<T>这不是一个很好的界面。例如,调用Add可能抛出(请参阅为什么数组实现IList?)。如果需要封装,最好使用IEnumerable<T>IReadOnlyList<T>


1

除了其他答案之外,了解使用LINQ时Enumerable与List / Array之间的区别可能会对性能产生巨大影响。简而言之,可以将Enumerable视为查询生成器,而List / Array是查询的结果。

在使用EntityFramework的LINQ to SQL的上下文中,前者只是构建SQL查询而不对数据库执行查询,也不将任何数据加载到内存中,而后者则相反。这就是为什么我们会推迟打电话.ToList()到内存中需要它来执行业务逻辑之前。

在其他情况下,返回IEnumerable的LINQ表达式将推迟执行直到.ToList()被调用。考虑下面的示例:

void Main()
{
    var w1 = "AB".AsEnumerable();
    Console.WriteLine($"W1: 1");
    w1 = w1.Where(W1);
    Console.WriteLine($"W1: 2");
    w1 = w1.Where(W2);
    Console.WriteLine($"W1: 3");
    w1.ToList();    
    Console.WriteLine($"----------");

    var w2 = "CD".AsEnumerable();
    Console.WriteLine($"W2: 1");
    w2 = w2.Where(W1);
    Console.WriteLine($"W2: 2");
    w2 = w2.ToList();
    Console.WriteLine($"W2: 3");
    w2 = w2.Where(W2);
    Console.WriteLine($"W2: 4");
    w2.ToList();    
    Console.WriteLine($"----------");
}

bool W1(char arg)
{
    Console.WriteLine($"W1:{arg}");
    return true;
}

bool W2(char arg)
{
    Console.WriteLine($"W2:{arg}");
    return true;
}

OUTPUT:
W1: 1
W1: 2
W1: 3
W1:A
W2:A
W1:B
W2:B
----------
W2: 1
W2: 2
W1:C
W1:D
W2: 3
W2: 4
W2:C
W2:D
----------

在第一个示例中,两个.Where()被“追加”并一起执行,最后一次.ToList()被调用时,首先通过管道先通过项目“ A ”,然后通过项目“ B ”,因此在输出中看到“ AABB ”,而在第二个示例中,在.Where()如果我们调用每次执行.ToList()随即,所以看到“ CD ”,然后选择“ CD ”了,输出的两倍。因此,每次将Enumerable转换为List或Array都会花费一个O(n)集合中的所有项目进行迭代,这将在集合很大时影响性能。

尽管我们不倾向于以这种方式.ToList()在LINQ调用之间调用来编写代码,但是当我们将代码放入返回的可重用方法中List/Array而不是返回时,这种情况会更频繁地发生。IEnumerable

但是,这并不意味着我们应该一直进行操作IEnumerable。正如towlson所提到的那样,诸如这样的操作.Count()将导致整个集合的迭代,而List或Array将预先计算此信息,因此,List/Array如果您打算.Count()多次调用,则转换为会更有效。这也是为什么.Count列表上有一个属性而不是一种对其.Count()进行计数的方法的原因。


0

这是旧帖子,但仍然想回复。IEnumerable是一种行为,而Array是数据结构(具有固定大小的元素的连续集合,便于通过索引访问元素)。当数组实现IEnumerable时,它也应该描述IEnumerable固有属性(以促进在集合上进行迭代)。

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.