IEnumerable
和Array
之间有什么区别?
IList
和List
之间有什么区别?
这些似乎具有相同的功能。
答案 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
是一个通用接口,许多类都使用它,例如Array
,List
和String
,以便让某些人遍历集合。基本上,它是驱动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固有属性(促进对集合的迭代)。