【问题标题】:Difference between foreach and for loops over an IEnumerable class in C#C# 中 IEnumerable 类的 foreach 和 for 循环之间的区别
【发布时间】:2008-09-04 17:20:16
【问题描述】:

有人告诉我,以下代码块之间存在性能差异。

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

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

在哪里

List<Entity> entityList;

我不是 CLR 期望的,但据我所知,它们应该归结为基本相同的代码。有没有人有混凝土(见鬼,我会拿走泥土)证据?

【问题讨论】:

    标签: c# performance loops


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

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

      Foreach 在技术上较慢,但更易于使用和阅读。除非性能很关键,否则我更喜欢 foreach 循环而不是 for 循环。

      【讨论】:

      • dnspy 调试器 - 方便在没有源代码的远程 Web 服务器上调试 .NET - 目前不会使用 F11 进入 foreach 循环(VS2017 调试器会这样) - 这可能会破坏交易,尤其是当 foreach 也较慢时。此外,在 Javascript 等某些语言中,foreach 有 2 种变体(例如数组和 for...in),而后者的行为并不总是那么直观,这会显着降低任何“更易于使用和更易于阅读”的好处。
      【解决方案3】:

      foreach 示例大致对应这段代码:

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

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

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

      【讨论】:

        【解决方案4】:

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

        IEnumerable 没有。

        如果您对接口 IEnumerable 进行编程并使用计数扩展方法,它将仅枚举元素以计数。

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

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

        如果您想要灵活性,请使用 foreach 并将程序设置为 IEnumerable。 (允许使用 linq 和/或 yield return)。

        【讨论】:

          【解决方案5】:

          在分配方面,最好看this blogpost。它准确显示了在什么情况下在堆上分配枚举数。

          【讨论】:

            【解决方案6】:

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

            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。唯一一次我认为这是一个大问题是链表之类的结构。

            【讨论】:

              【解决方案7】:
              For Loop
              for loop is used to perform the opreration n times
              for(int i=0;i<n;i++)
              {
              l=i;
              }
              foreach loop
              
              int[] i={1,2,3,4,5,6}
              foreach loop is used to perform each operation value/object in IEnumarable 
              foreach(var k in i)
              {
              l=k;
              }
              

              【讨论】:

                猜你喜欢
                • 2015-11-18
                • 2017-01-05
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-07-08
                • 2019-01-11
                • 1970-01-01
                相关资源
                最近更新 更多