C#中IEnumerable类的foreach和for循环之间的区别

我被告知以下代码块之间存在性能差异。

foreach (Entity e in entityList) { .... } 

 for (int i=0; i<entityList.Count; i++) { Entity e = (Entity)entityList[i]; ... } 

哪里

 List entityList; 

我不是CLR的期望,但从我可以告诉他们应该归结为基本相同的代码。 有没有人有这样或那样的混凝土(哎呀,我会把包装好的污垢)证据?

foreach创建一个枚举器的实例(从GetEnumerator返回),该枚举器在整个foreach循环过程中也保持状态。 然后,它重复调用枚举器上的Next()对象,并为它返回的每个对象运行代码。

它们不会以任何方式归结为相同的代码,真的,如果您编写自己的枚举器,您会看到它们。

这是一篇很好的文章,显示了两个循环之间的IL差异。

Foreach在技术上较慢,但更容易使用和更容易阅读。 除非性能至关重要,否则我更喜欢fore循环而不是for循环。

foreach示例大致对应于此代码:

 using(IEnumerator e = entityList.GetEnumerator()) { while(e.MoveNext()) { Entity entity = e.Current; ... } } 

这里有两个成本,一个常规for循环不需要支付:

  1. 通过entityList.GetEnumerator()分配枚举器对象的成本。
  2. 两个虚方法的成本调用(MoveNext和Current)列表的每个元素。

这里错过了一点:List有一个Count属性,它在内部跟踪其中有多少元素。

一个IEnumerable没有。

如果你编程到IEnumerable接口并使用count extention方法,它将枚举只计算元素。

虽然在IEnumerable中你不能通过索引引用项目,但这是一个有争议的问题。

因此,如果您想要锁定列表和数组,您可以获得较小的性能提升。

如果你想要灵活性,可以使用foreach和program to IEnumerable。 (允许使用linq和/或收益率)。

在分配方面,最好看一下这篇博文 。 它精确地显示了在堆上分配枚举数的情况。

我认为可能获得性能提升的一种可能情况是可枚举类型的大小和循环条件是否为常数; 例如:

 const int ArraySize = 10; int[] values = new int[ArraySize]; //... for (int i = 0; i 

在这种情况下,根据循环体的复杂性,编译器可能能够使用内联调用替换循环。 我不知道.NET编译器是否这样做,如果可枚举类型的大小是动态的,它的实用性有限。

foreach可能表现更好的一种情况是数据结构,如链接列表,其中随机访问意味着遍历列表; foreach使用的枚举器可能一次迭代一个项目,使每个访问O(1)和完整循环O(n),但调用索引器意味着从头开始并在正确的索引处找到项目; 每个循环O(N)为O(n ^ 2)。

就个人而言,我通常不会担心它,并且在我需要所有物品并且不关心物品的索引时使用foreach 。 如果我不使用所有项目或者我真的需要知道索引,我会使用。 我唯一能看到它是一个大问题的是像链接列表这样的结构。

 For Loop for loop is used to perform the opreration n times for(int i=0;i