在某些情况下,Foreach比For更快?

时间:2017-10-29 06:40:26

标签: c# arrays for-loop foreach

我不喜欢过早优化,但在做一个简单的任务时我很好奇 所以我添加了一个秒表。 我不明白这种差异有多大。

每个字符串数组(7个字符)[richtextbox.text]。

阵列长度:5500个元素。

Foreach Elapsed Time: 0.0015秒

经过时间: 9.757秒

有关:

if (chkLineBreaks.Checked)
{
    for (int i = 0; i < txtInput.Lines.Length; i++)
    {
        outputStringBuilder.Append($@"'{txtInput.Lines[i]}',");
    }
}

FOREACH:

foreach (var line in txtInput.Lines)
{
    outputStringBuilder.Append($@"'{line}',");
    if (chkLineBreaks.Checked)
        outputStringBuilder.AppendLine();
}

从我所看到的,差异应该可以忽略不计,For会略快一些。

更重要的是,foreach在每次迭代中都有一个条件(除非它在循环之前被'提升'。

这里发生了什么?

编辑: 我已将foreach代码更改为:

int i = 0;
foreach (var line in txtInput.Lines)
{
    outputStringBuilder.Append($@"'{txtInput.Lines[i]}',");
    i++;
}

所以它现在正在做同样的事情。 它需要 4.625秒 ..仍然是FOR

的一半时间

另外我知道我可以在循环外提取数组但这不是我在这里测试的内容:)

编辑#2: 这是该部分的完整代码:

Stopwatch sw = new Stopwatch();
        sw.Start();
        //    for (int i = 0; i < txtInput.Lines.Length; i++)
        //    {
        //        outputStringBuilder.Append($@"'{txtInput.Lines[i]}',");
        //    }
        int i = 0;
        foreach (var line in txtInput.Lines)
        {
            outputStringBuilder.Append($@"'{txtInput.Lines[i]}',");
            i++;
        }
        MessageBox.Show(sw.Elapsed.ToString());

2 个答案:

答案 0 :(得分:3)

问题是txtInput.Linesfor循环中执行多次(每行一次)(由于使用了txtInput.Lines[i])。 因此,对于文件的每一行,您都说'好,请将此文本框解析为多行 - 然后将第n行解析为' - 并且解析是杀手位。

为了更公平的比较:

if (chkLineBreaks.Checked)
{
    var lines = txtInput.Lines;
    for (int i = 0; i < lines.Length; i++)
    {
        outputStringBuilder.Append($@"'{lines[i]}',");
    }
}

这样Lines调用只执行一次(即相当于foreach场景)。

发现这类问题的一种方法是比较时间。较慢的一个比快速的慢约6K,你有5.5K的条目。由于5.5K和6K是非常相似的数字,它可能会促使你思考'我在循环中做了什么我不应该做的事情?'

答案 1 :(得分:1)

编译代码在遍历数组(或列表)时,forforeach语句之间的差别很小。

考虑这个简单的代码,用三种不同的方式写出字符串列表:

class Program
{
    static void Main(string[] args)
    {
        var list = Enum.GetNames(typeof(System.UriComponents));

        // 1. for each
        foreach (var item in list)
        {
            Console.WriteLine(item);
        }
        // 2. for loop
        for (int i = 0; i<list.Length; i++)
        {
            Console.WriteLine(list[i]);
        }
        // 3. LINQ
        Console.WriteLine(string.Join(Environment.NewLine, list));
    }
}

现在查看MSIL已编译的代码,使用C#ILSpy将其翻译回DotNetPeek

// ConsoleApplication1.Program
private static void Main(string[] args)
{
    string[] list = Enum.GetNames(typeof(UriComponents));
    string[] array = list;
    for (int j = 0; j < array.Length; j++)
    {
        string item = array[j];
        Console.WriteLine(item);
    }
    for (int i = 0; i < list.Length; i++)
    {
        Console.WriteLine(list[i]);
    }
    Console.WriteLine(string.Join(Environment.NewLine, list));
}

请参阅两个for循环。 foreach语句由编译器变为for循环。至于string.Join()语句,它调用SZArrayEnumerator来保存对数组的引用,以及当前索引值。在每次.MoveNext()调用时,索引会递增并返回一个新值。基本上,它等同于以下内容:

int i = 0;
while (i<list.Length)
{
    Console.WriteLine(list[i]);
    i++;
}