在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

(编辑)

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


当前回答

    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

其他回答

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

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

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

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

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

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

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

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

这可以节省你:

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);
}

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

两者之间不太可能有巨大的性能差异。与往常一样,当面对“哪个更快?”的问题时,您应该始终认为“我可以测量这个”。

在循环体中编写两个做相同事情的循环,执行并计时,并查看速度的差异。使用一个几乎为空的主体和一个与您实际要做的类似的循环主体来执行此操作。还可以尝试使用您正在使用的集合类型,因为不同类型的集合可能具有不同的性能特征。

在大多数情况下,其实没有什么区别。

通常,当你没有显式的数值索引时,你总是必须使用foreach,而当你实际上没有可迭代集合时,你总是必须使用for(例如,在上三角形的二维数组网格上迭代)。在某些情况下,你可以做出选择。

有人可能会说,如果代码中开始出现神奇的数字,for循环可能会更难维护。你对不能使用for循环而不得不构建一个集合或使用lambda来构建子集合感到恼火应该是正确的,因为for循环已经被禁止了。