Difference between foreach and for loops over an IEnumerable class in C#
我被告知以下代码块之间存在性能差异。
1 2 3 4 | foreach (Entity e in entityList) { .... } |
和
1 2 3 4 5 | for (int i=0; i<entityList.Count; i++) { Entity e = (Entity)entityList[i]; ... } |
号
在哪里?
1 | List<Entity> entityList; |
我不是clr所期望的,但从我所能知道的来看,它们应该归结为基本上相同的代码。有没有人有确凿的证据(见鬼,我会拿包装好的泥土)来证明?
foreach创建枚举器的实例(从getEnumerator返回),该枚举器在foreach循环的整个过程中也保持状态。然后它反复调用枚举器上的next()对象,并为它返回的每个对象运行代码。
它们不会以任何方式归结为相同的代码,实际上,如果编写自己的枚举器,您会看到这一点。
这是一篇很好的文章,展示了两个循环之间的IL差异。
foreach在技术上速度较慢,但更易于使用和阅读。除非性能至关重要,否则我更喜欢foreach循环而不是for循环。
foreach示例大致对应于以下代码:
1 2 3 4 5 6 | using(IEnumerator<Entity> e = entityList.GetEnumerator()) { while(e.MoveNext()) { Entity entity = e.Current; ... } } |
号
这里有两个费用,一个普通的for循环不需要支付:
这里漏了一点:列表有一个Count属性,它在内部跟踪其中有多少元素。
IEnumerable没有。
如果您编程到接口IEnumerable并使用Count扩展方法,它将枚举以计数元素。
但这是一个无意义的点,因为在IEnumerable中不能按索引引用项。
因此,如果您想锁定到列表和数组中,您可以获得较小的性能提升。
如果你想灵活使用foreach和程序到ienumerable。(允许使用LINQ和/或产量返回)。
在分配方面,最好看一下这个blogpost。它精确地显示了在什么情况下在堆上分配枚举器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
我认为一种可能的情况是,如果可枚举类型的大小和循环条件为常量,则可能获得性能提升;例如:
1 2 3 4 5 6 |
在这种情况下,根据循环体的复杂性,编译器可能能够用内联调用替换循环。我不知道.NET编译器是否会这样做,如果可枚举类型的大小是动态的,那么它的实用程序是有限的。
就我个人而言,我通常不担心它,在我需要所有项目的任何时候使用