在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

(编辑)

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


当前回答

我发现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);
    }

其他回答

for是否比foreach快确实不是重点。我非常怀疑选择其中之一会对你的表现产生重大影响。

优化应用程序的最佳方法是对实际代码进行分析。这将精确地找出占用最多工作/时间的方法。首先优化它们。如果性能仍然不能接受,请重复上述步骤。

一般来说,我建议不要进行微观优化,因为它们很少会产生任何显著的收益。唯一的例外是在优化确定的热门路径时(即,如果您的分析确定了一些高度使用的方法,那么广泛地优化这些方法可能是有意义的)。

有很好的理由选择foreach循环而不是for循环。如果你可以使用foreach循环,你的老板说你应该这样做是对的。

然而,并不是每次迭代都是简单地按顺序逐个遍历列表。如果他是禁止的,是的,那是错误的。

如果我是你,我会把你所有的for循环转化成递归。这样可以教会他,对你来说也是很好的脑力锻炼。

    internal static void Test()
    {
        int LOOP_LENGTH = 10000000;
        Random random = new Random((int)DateTime.Now.ToFileTime());

        {
            Dictionary<int, int> dict = new Dictionary<int, int>();
            long first_memory = GC.GetTotalMemory(true);
            var stopWatch = Stopwatch.StartNew();
            for (int i = 0; i < 64; i++)
            {
                dict.Add(i, i);
            }

            for (int i = 0; i < LOOP_LENGTH; i++)
            {
                for (int k = 0; k < dict.Count; k++)
                {
                    if (dict[k] > 1000000) Console.WriteLine("Test");
                }
            }
            stopWatch.Stop();
            var last_memory = GC.GetTotalMemory(true);
            Console.WriteLine($"Dictionary for T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");

            GC.Collect();
        }


        {
            Dictionary<int, int> dict = new Dictionary<int, int>();
            long first_memory = GC.GetTotalMemory(true);
            var stopWatch = Stopwatch.StartNew();
            for (int i = 0; i < 64; i++)
            {
                dict.Add(i, i);
            }

            for (int i = 0; i < LOOP_LENGTH; i++)
            {
                foreach (var item in dict)
                {
                    if (item.Value > 1000000) Console.WriteLine("Test");
                }
            }
            stopWatch.Stop();
            var last_memory = GC.GetTotalMemory(true);
            Console.WriteLine($"Dictionary foreach T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");

            GC.Collect();
        }

        {
            Dictionary<int, int> dict = new Dictionary<int, int>();
            long first_memory = GC.GetTotalMemory(true);
            var stopWatch = Stopwatch.StartNew();
            for (int i = 0; i < 64; i++)
            {
                dict.Add(i, i);
            }

            for (int i = 0; i < LOOP_LENGTH; i++)
            {
                foreach (var item in dict.Values)
                {
                    if (item > 1000000) Console.WriteLine("Test");
                }
            }
            stopWatch.Stop();
            var last_memory = GC.GetTotalMemory(true);
            Console.WriteLine($"Dictionary foreach values T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");

            GC.Collect();
        }


        {
            List<int> dict = new List<int>();
            long first_memory = GC.GetTotalMemory(true);
            var stopWatch = Stopwatch.StartNew();
            for (int i = 0; i < 64; i++)
            {
                dict.Add(i);
            }

            for (int i = 0; i < LOOP_LENGTH; i++)
            {
                for (int k = 0; k < dict.Count; k++)
                {
                    if (dict[k] > 1000000) Console.WriteLine("Test");
                }
            }
            stopWatch.Stop();
            var last_memory = GC.GetTotalMemory(true);
            Console.WriteLine($"list for T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");

            GC.Collect();
        }


        {
            List<int> dict = new List<int>();
            long first_memory = GC.GetTotalMemory(true);
            var stopWatch = Stopwatch.StartNew();
            for (int i = 0; i < 64; i++)
            {
                dict.Add(i);
            }

            for (int i = 0; i < LOOP_LENGTH; i++)
            {
                foreach (var item in dict)
                {
                    if (item > 1000000) Console.WriteLine("Test");
                }
            }
            stopWatch.Stop();
            var last_memory = GC.GetTotalMemory(true);
            Console.WriteLine($"list foreach T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");

            GC.Collect();
        }
    }

T:10.1957728s M:2080的字典 字典T:10.5900586 M:1952 字典foreach值T:3.8294776s M:2088 T:3.7981471s M:320 T:4.4861377s M:648

你可以在Deep .NET - part 1迭代中读到它

它覆盖了从。net源代码一直到反汇编的结果(没有第一次初始化)。

数组迭代使用foreach循环:

And - list迭代foreach循环:

最终结果是:

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

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

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

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

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