在c# / VB.NET/。哪个循环运行得更快,for还是foreach?

自从很久以前我读到for循环比foreach循环工作得快,我就认为它适用于所有集合、泛型集合、所有数组等。

我搜索了谷歌,找到了几篇文章,但大多数都是不确定的(阅读文章评论),而且是开放式的。

理想的情况是列出每种情况以及最佳解决方案。

例如(这只是一个例子):

用于迭代1000+的数组 字符串- for比foreach好 对于迭代IList(非泛型)字符串- foreach更好 比

在网上找到了一些相同的参考资料:

由Emmanuel Schanzer撰写的原创文章 CodeProject FOREACH Vs. FOR 博客——去博客还是不去博客,这是个问题 ASP。NET论坛- NET 1.1 c# for vs foreach

(编辑)

除了可读性之外,我对事实和数据真的很感兴趣。在某些应用中,最后一英里的性能优化确实很重要。


当前回答

我不期望任何人发现两者之间的“巨大”性能差异。

我想答案取决于您试图访问的集合是否具有更快的indexer访问实现或更快的IEnumerator访问实现。由于IEnumerator经常使用索引器,并且仅保存当前索引位置的副本,因此我希望枚举器访问至少与直接索引访问一样慢或更慢,但不会慢太多。

当然,这个答案没有解释编译器可能实现的任何优化。

其他回答

每当有关于性能的争论时,您只需要编写一个小测试,以便您可以使用量化结果来支持您的案例。

使用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

幸运的是,这样做的结果表明差异可以忽略不计,您还可以在最可维护的代码中执行任何结果

我发现foreach循环迭代列表更快。下面是我的测试结果。在下面的代码中,我分别迭代一个大小为100、10000和100000的数组,使用for和foreach循环来测量时间。

private static void MeasureTime()
    {
        var array = new int[10000];
        var list = array.ToList();
        Console.WriteLine("Array size: {0}", array.Length);

        Console.WriteLine("Array For loop ......");
        var stopWatch = Stopwatch.StartNew();
        for (int i = 0; i < array.Length; i++)
        {
            Thread.Sleep(1);
        }
        stopWatch.Stop();
        Console.WriteLine("Time take to run the for loop is {0} millisecond", stopWatch.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("Array Foreach loop ......");
        var stopWatch1 = Stopwatch.StartNew();
        foreach (var item in array)
        {
            Thread.Sleep(1);
        }
        stopWatch1.Stop();
        Console.WriteLine("Time take to run the foreach loop is {0} millisecond", stopWatch1.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("List For loop ......");
        var stopWatch2 = Stopwatch.StartNew();
        for (int i = 0; i < list.Count; i++)
        {
            Thread.Sleep(1);
        }
        stopWatch2.Stop();
        Console.WriteLine("Time take to run the for loop is {0} millisecond", stopWatch2.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("List Foreach loop ......");
        var stopWatch3 = Stopwatch.StartNew();
        foreach (var item in list)
        {
            Thread.Sleep(1);
        }
        stopWatch3.Stop();
        Console.WriteLine("Time take to run the foreach loop is {0} millisecond", stopWatch3.ElapsedMilliseconds);
    }

更新

在@jgauffin建议后,我使用了@johnskeet代码,发现使用数组的for循环比下面的更快,

Foreach循环与数组。 For带列表的循环。 Foreach循环与列表。

请看下面我的测试结果和代码,

private static void MeasureNewTime()
    {
        var data = new double[Size];
        var rng = new Random();
        for (int i = 0; i < data.Length; i++)
        {
            data[i] = rng.NextDouble();
        }
        Console.WriteLine("Lenght of array: {0}", data.Length);
        Console.WriteLine("No. of iteration: {0}", Iterations);
        Console.WriteLine(" ");
        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 with Array: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (var 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 with Array: {0}", sw.ElapsedMilliseconds);
        Console.WriteLine(" ");

        var dataList = data.ToList();
        sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            double sum = 0;
            for (int j = 0; j < dataList.Count; j++)
            {
                sum += data[j];
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("For loop with List: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            double sum = 0;
            foreach (double d in dataList)
            {
                sum += d;
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("Foreach loop with List: {0}", sw.ElapsedMilliseconds);
    }

Foreach循环比for循环展示了更具体的意图。

使用foreach循环向使用您代码的任何人表明,您计划对集合中的每个成员执行一些操作,而不管其在集合中的位置。它还显示您没有修改原始集合(如果您试图修改,则会抛出异常)。

foreach的另一个优点是它适用于任何IEnumerable,而as for只适用于IList,其中每个元素实际上都有一个索引。

但是,如果需要使用元素的索引,那么当然应该允许使用for循环。但是如果您不需要使用索引,那么使用索引只会使您的代码变得混乱。

据我所知,这对性能没有重大影响。在未来的某个阶段,使用foreach调整代码以在多核上运行可能会更容易,但现在还不需要担心这一点。

这可以节省你:

public IEnumerator<int> For(int start, int end, int step) {
    int n = start;
    while (n <= end) {
        yield n;
        n += step;
    }
}

Use:

foreach (int n in For(1, 200, 4)) {
    Console.WriteLine(n);
}

为了获得更大的胜利,你可以把三名代表作为参数。

完全禁止使用for循环之类的东西似乎有点奇怪。

这里有一篇有趣的文章,介绍了两个循环之间的许多性能差异。

我个人会说,我发现foreach比for循环更具可读性,但你应该使用最好的方法来完成手头的工作,如果for循环更合适的话,就不必编写额外的长代码来包含foreach循环。