在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

(编辑)

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


当前回答

你可以用一个IQueryable .foreach闭包来代替:

myList.ForEach(c => Console.WriteLine(c.ToString());

其他回答

我的猜测是,在99%的情况下,它可能并不重要,所以为什么要选择更快的,而不是最合适的(最容易理解/维护)?

至少我没有看到我的同事或上级这么说,考虑到for和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

一种强大而精确的测量时间的方法是使用BenchmarkDotNet库。

在下面的示例中,我在for/foreach上对1,000,000,000个整数记录进行了循环,并使用BenchmarkDotNet进行了测量:

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

public class Program
{
    public static void Main()
    {
        BenchmarkRunner.Run<LoopsBenchmarks>();
    }
}

[MemoryDiagnoser]
public class LoopsBenchmarks
{
    private List<int> arr = Enumerable.Range(1, 1_000_000_000).ToList();

    [Benchmark]
    public void For()
    {
        for (int i = 0; i < arr.Count; i++)
        {
            int item = arr[i];
        }
    }

    [Benchmark]
    public void Foreach()
    {
        foreach (int item in arr)
        {
        }
    }
}

结果如下:

结论

在上面的例子中,我们可以看到for循环比foreach循环略快。我们还可以看到两者使用相同的内存分配。

我遇到了一个案子,foreach比For快得多

为什么foreach在读取richtextbox行时比for循环快

我有一个类似于那个问题中的OP的案例。

A textbox reading about 72K lines, and I was accessling the Lines property(which is actually a getter method). (And apparently often in winforms there are getter methods that aren't O(1). I suppose it's O(n), so the larger the textbox the longer it takes to get a value from that 'property'. And in the for loop I had as the OP there had for(int i=0;i<textBox1.lines.length;i++) str=textBox1.Lines[i] , and it was really quite slow as it was reading the entire textbox each time it read a line plus it was reading the entire textbox each time it checked the condition.

Jon Skeet演示了您可以只访问一次Lines属性(甚至不是每次迭代一次,只是一次)。而不是每次迭代两次(这是大量的次数)。Do string[] strarlines = textBox1.Lines;然后在星线间循环。

但是一个直观形式的for循环访问Lines属性是非常低效的

for (int i = 0; i < richTextBox.Lines.Length; i++)
{
    s = richTextBox.Lines[i];
}

对于文本框,或者富文本框,它非常慢。

OP在一个富文本框上测试了这个循环,发现“有15000行。For循环花了8分钟才循环到15000行。而foreach只花了不到一秒钟的时间来列举它。”

那个链接的OP发现这个foreach比上面提到的他(同一个OP)的for循环要有效得多。就像我一样。

   String s=String.Empty;
   foreach(string str in txtText.Lines)
    {
       s=str;
    }