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

时间:2009-04-19 02:58:22

标签: c#

IEnumerableArray之间有什么区别?

IListList之间有什么区别?

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

8 个答案:

答案 0 :(得分:95)

IEnumerable仅提供最小的“可迭代”功能。你可以遍历序列,但这就是它。这有缺点 - 例如,使用IEnumerable计算元素或获取第n个元素是非常低效的 - 但它也有优势 - 例如,IEnumerable可能是无穷无尽的序列,如素数序列。

Array是一个固定大小的集合,具有随机访问权限(即您可以将其编入索引)。

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

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

答案 1 :(得分:16)

IEnumerable是一个允许迭代项目集合的接口(例如,通过foreach关键字)。

数组是.NET内在函数。它包含相同类型的项目,但它具有固定的大小。使用x元素创建数组后,它无法增长或缩小。

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

List实现IList接口;这是一个具体的清单。

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

IList&数组都实现了IEnumerable。这就是接口工作的方式 - 类实现契约并以类似的方式运行,并且可以作为结果进行类似处理(您知道该类实现IEnumerable,您不需要知道方法或者为什么)。我建议你阅读界面等等。

答案 2 :(得分:10)

IEnumerable和IList是interfaces。数组和列表是类。 Array实现IEnumerable。 List实现IList,它扩展了IEnumerable。

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

答案 3 :(得分:6)

生成IEnumerable集合很懒惰。 例如:

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

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

答案 4 :(得分:2)

IEnumerable是一个通用接口,许多类都使用它,例如ArrayListString,以便让某些人遍历集合。基本上,它是驱动foreach语句的原因。

IList通常是向最终用户公开List类型变量的方式。此接口允许随机访问底层集合。

答案 5 :(得分:1)

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

那是因为List<T>.GetEnumerator返回的迭代器对象是值类型,而IList<T>.GetEnumerator返回的迭代器对象是引用类型,因此需要内存分配(参见{{ 3}})。

在我看来,IList<T>无论如何都不是一个非常好的界面。例如,调用Add可以抛出(参见Enumerator of value type of list in c#)。如果您需要封装,最好使用IEnumerable<T>IReadOnlyList<T>

答案 6 :(得分: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 ”,而在第二个示例中,每次调用{{1 }}之后,因此看到两次“ CD ”,然后再次看到“ CD ”。因此,每次将Enumerable转换为List或Array时,对集合中的所有项目进行一次 O(n)迭代,当集合很大时会对性能产生影响。

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

但这并不意味着我们应该始终对.ToList()进行操作。如towlson所述,诸如List/Array之类的操作将导致集合上的迭代,而List或Array将预先计算此信息,因此,如果满足以下条件,则转换为IEnumerable会更有效您计划多次致电IEnumerable。这也是为什么列表上有.Count()属性而不是List/Array方法进行计数的原因。

答案 7 :(得分:0)

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

相关问题