在C#/ VB.NET/.NET中,哪个循环运行得更快,for
或者foreach
?
自从我读了一个for
循环工程快于foreach
环路很久以前我以为这对所有集合,泛型集合,所有阵列,等真正站在
我搜索谷歌并发现了一些文章,但其中大多数都没有结果(阅读文章的评论)和开放式.
什么是理想的是列出每个场景和相同的最佳解决方案.
例如(只是它应该如何的一个例子):
迭代1000多个字符串的数组 - for
比...更好foreach
迭代IList
(非泛型)字符串 - foreach
比...更好for
在网上找到的一些参考文献:
Emmanuel Schanzer的原创宏伟文章
CodeProject FOREACH Vs. 对于
博客 - foreach
或不是foreach
,这就是问题
ASP.NET论坛 - NET 1.1 C#for
vsforeach
[编辑]
除了可读性方面,我对事实和数据非常感兴趣.有些应用程序的最后一英里性能优化受到挤压很重要.
Patrick Smacchia上个月在博客上写了这个结论,得出以下结论:
List上的for循环比List上的foreach循环便宜2倍多.
在数组上循环比在List上循环便宜约2倍.
因此,使用for循环on array比使用foreach在List上循环便宜5倍(我相信,这就是我们所做的).
首先,反驳德米特里的答案.对于数组,C#编译器发出的代码foreach
与用于等效for
循环的代码大致相同.这就解释了为什么对于这个基准测试,结果基本相同:
using System; using System.Diagnostics; using System.Linq; class Test { const int Size = 1000000; const int Iterations = 10000; static void Main() { double[] data = new double[Size]; Random rng = new Random(); for (int i=0; i < data.Length; i++) { data[i] = rng.NextDouble(); } double correctSum = data.Sum(); Stopwatch sw = Stopwatch.StartNew(); for (int i=0; i < Iterations; i++) { double sum = 0; for (int j=0; j < data.Length; j++) { sum += data[j]; } if (Math.Abs(sum-correctSum) > 0.1) { Console.WriteLine("Summation failed"); return; } } sw.Stop(); Console.WriteLine("For loop: {0}", sw.ElapsedMilliseconds); sw = Stopwatch.StartNew(); for (int i=0; i < Iterations; i++) { double sum = 0; foreach (double d in data) { sum += d; } if (Math.Abs(sum-correctSum) > 0.1) { Console.WriteLine("Summation failed"); return; } } sw.Stop(); Console.WriteLine("Foreach loop: {0}", sw.ElapsedMilliseconds); } }
结果:
For loop: 16638 Foreach loop: 16529
接下来,验证Greg关于集合类型的重要性 - 将数组更改List
为上面的a,您会得到截然不同的结果.它不仅总体上明显变慢,而且foreach变得比通过索引访问慢得多.话虽如此,我仍然总是喜欢foreach到for循环,它使代码更简单 - 因为可读性几乎总是重要的,而微优化很少.
foreach
循环演示比for
循环更具体的意图.
使用foreach
循环向使用您的代码的任何人演示,您计划对集合中的每个成员执行某些操作,而不管其在集合中的位置.它还表明您没有修改原始集合(如果您尝试,则会抛出异常).
另一个优点foreach
是它适用于任何IEnumerable
,for
只有在有意义的IList
地方,每个元素实际上有一个索引.
但是,如果需要使用元素的索引,那么当然应该允许使用for
循环.但是如果你不需要使用索引,那么只有一个索引会使代码混乱.
据我所知,没有重大的性能影响.在未来的某个阶段,使用foreach
多个内核运行代码可能更容易,但现在不用担心.
任何时候都有关于性能的争论,你只需要编写一个小测试,这样你就可以使用定量结果来支持你的情况.
为了准确,使用StopWatch类并重复几百万次.(没有for循环,这可能很难):
using System.Diagnostics; //... Stopwatch sw = new Stopwatch() sw.Start() for(int i = 0; i < 1000000;i ++) { //do whatever it is you need to time } sw.Stop(); //print out sw.ElapsedMilliseconds
手指越过了这个节目的结果,差异可以忽略不计,你可能只是在最易维护的代码中做任何结果
它总是很接近.对于一个数组,有时 for
会稍微快一点,但foreach
更具表现力,并提供LINQ等.一般来说,坚持使用foreach
.
另外,foreach
可以在某些情况下进行优化.例如,链接列表可能是索引器可怕的,但它可能很快foreach
.实际上,由于LinkedList
这个原因,该标准甚至没有提供索引器.
我的猜测是,在99%的情况下它可能不会很重要,那么为什么你会选择更快而不是最合适的(最容易理解/维护)?
两者之间不太可能存在巨大的性能差异.一如既往,面对"哪个更快?" 问题,你应该总是想"我可以衡量这一点."
编写两个在循环体中执行相同操作的循环,对它们执行和计时,并查看速度的差异.用一个几乎空的身体和一个类似于你实际做的循环体来做这件事.也可以使用您正在使用的集合类型来尝试它,因为不同类型的集合可以具有不同的性能特征.
有很好的理由喜欢 foreach
循环遍历for
循环.如果你可以使用foreach
循环,你的老板是对的.
但是,并非每次迭代都只是逐个按顺序遍历列表.如果他是禁止的,那是错的.
如果我是你,我会做的就是把所有你自然的循环变成递归.那教会了他,这对你来说也是一种很好的心理锻炼.
Jeffrey Richter在TechEd 2005上:
"多年来,我开始学习C#编译器对我来说基本上是骗子." ......"它涉及许多事情." .."就像你做一个foreach循环......"......"...这是你编写的一小段代码,但C#编译器为了做到这一点而吐出来的是它的现象.它推出了一个在那里尝试/ finally块,在finally块内部,它将你的变量转换为IDisposable接口,如果转换为suceeds,则调用它上面的Dispose方法,在循环内部,它在循环内部重复调用Current属性和MoveNext方法,在封面下创建了对象.很多人都使用foreach,因为它很容易编码,很容易做到.".."foreach在性能方面不是很好,如果你在一个集合上迭代而不是使用square括号表示法,只是做索引,这只是更快,而且它不会在堆上创建任何对象..."
按需网络直播:http://msevents.microsoft.com/CUI/WebCastEventDetails.aspx? EventID = 1032292286&EventCategory = 3& culture = en -US&CountryCode = US
这是荒唐的.禁止for循环,性能方面或其他方面没有令人信服的理由.
请参阅Jon Skeet的博客,了解性能基准和其他参数.
如果您使用对象集合,foreach
则更好,但如果增加数字,则for
循环更好.
请注意,在最后一种情况下,您可以执行以下操作:
foreach (int i in Enumerable.Range(1, 10))...
但它肯定表现不佳,与a相比,它的性能实际上更差for
.
这应该可以节省你:
public IEnumeratorFor(int start, int end, int step) { int n = start; while (n <= end) { yield n; n += step; } }
使用:
foreach (int n in For(1, 200, 4)) { Console.WriteLine(n); }
为获得更大的胜利,您可以将三名代表作为参数.
当你循环遍历数组,列表等常见结构时,a for
和a foreach
循环中的速度差异很小,并且LINQ
对集合进行查询几乎总是稍微慢一些,尽管写起来更好!正如其他海报所说,追求表现力而不是毫秒的额外表现.
到目前为止还没有说过的是,当foreach
编译循环时,编译器会根据迭代的集合对其进行优化.这意味着当你不确定要使用哪个循环时,你应该使用foreach
循环 - 它会在编译时为你生成最好的循环.它也更具可读性.
foreach
循环的另一个关键优势是,如果您的集合实现发生更改(例如从int array
到a List
),那么您的foreach
循环将不需要任何代码更改:
foreach (int i in myCollection)
无论你的集合是什么类型,上面都是一样的,而在你的for
循环中,如果你myCollection
从a array
变为a ,则不会构建以下内容List
:
for (int i = 0; i < myCollection.Length, i++)
它可能取决于您枚举的集合类型及其索引器的实现.但总的来说,使用foreach
可能是一种更好的方法.
此外,它将适用于任何IEnumerable
- 不仅仅是索引器的东西.
"我可以用任何论据来帮助我说服他使用for循环吗?"
不,如果你的老板微观管理到告诉你使用什么编程语言的水平,你真的没什么可说的.抱歉.
这与大多数"更快"的问题具有相同的两个答案:
1)如果你不测量,你不知道.
2)(因为...)取决于.
这取决于"MoveNext()"方法的成本,相对于"this [int index]"方法的成本,对于您将迭代的IEnumerable的类型(或类型).
"foreach"关键字是一系列操作的简写 - 它在IEnumerable上调用GetEnumerator()一次,每次迭代调用一次MoveNext(),它进行一些类型检查,依此类推.最有可能影响性能测量的是MoveNext()的成本,因为它被调用O(N)次.也许它很便宜,但也许不是.
"for"关键字看起来更可预测,但在大多数"for"循环中,您会发现类似"collection [index]"的内容.这看起来像一个简单的数组索引操作,但它实际上是一个方法调用,其成本完全取决于您迭代的集合的性质.可能它很便宜,但也许不是.
如果集合的底层结构本质上是一个链表,那么MoveNext很便宜,但索引器可能有O(N)成本,这使得"for"循环的真实成本为O(N*N).
每种语言结构都有适当的使用时间和地点.C#语言有四个独立的迭代语句是有原因的- 每个语句都有特定用途,并且具有适当的用途.
我建议与老板坐下来,试着理性地解释为什么for
循环有目的.有时for
迭代块更清楚地描述算法而不是foreach
迭代.如果是这样,则使用它们是合适的.
我还要向你的老板指出 - 性能不是,也不应该是任何实际的问题 - 更多的是以简洁,有意义,可维护的方式表达算法.像这样的微优化完全错过了性能优化,因为任何真正的性能优势都来自算法重新设计和重构,而不是循环重构.
如果经过理性的讨论,仍然存在这种威权观点,那么由你来决定如何进行.就个人而言,我不乐意在一个不鼓励理性思考的环境中工作,并考虑转到不同雇主的另一个职位.但是,我强烈建议在讨厌之前进行讨论 - 可能只是存在一个简单的误解.
除了这一点之外,是否for
比foreach
实际更快.我严重怀疑选择一个会对你的表现产生重大影响.
优化应用程序的最佳方法是通过分析实际代码.这将确定占用大部分工作/时间的方法.首先优化它们.如果性能仍然不可接受,请重复此过程.
作为一般规则,我建议远离微观优化,因为它们很少会产生任何重大收益.唯一的例外是在优化已识别的热路径时(即,如果您的分析确定了一些高度使用的方法,那么广泛优化这些方法可能是有意义的).
这是你在循环中做的影响性能的东西,而不是实际的循环结构(假设你的情况是非平凡的).
您可以在Deep .NET-第1部分迭代中了解它
它涵盖了.NET源代码一直到反汇编的结果(没有第一次初始化)。
例如-具有foreach循环的数组迭代:
和-使用foreach循环列出迭代:
最终结果: