【问题标题】:C# loops: iterating through an arrayC# 循环:遍历数组
【发布时间】:2013-02-27 14:21:26
【问题描述】:

如果我有如下循环:

foreach (string pass in new string[] { "pass1", "pass2", "pass3" })
{
 x = pass; //etc
}

匿名字符串数组是最初创建一次,还是每次传递都重新创建一次?

我相信前者,但同事们相信这是一个等待发生的错误,因为他们说 foreach 循环的每次迭代都会导致创建一个新的字符串数组。

VS 反汇编代码表明我是对的,但我想确定一下。

我们正在研究这个问题的原因是试图了解一个神秘的错误,该错误报告一个集合在迭代时已被更改。

【问题讨论】:

  • 您可以轻松地对其进行测试,例如使用 DateTime 值代替字符串,并在每次迭代中检查成员。
  • 另一种向他们展示它只评估一次的方法是在调试模式下单步执行代码。创建字符串数组的代码只会高亮一次。
  • new string[] { "pass1", "pass" + i++, "pass3" }
  • “一个神秘的错误,它报告集合在迭代时已被更改” - 为什么不将其作为问题发布?
  • “一个神秘的错误,它报告集合在迭代时已被更改。” - 这表明您有从循环内的集合中删除项目的代码。这就是你应该寻找的东西

标签: c# loops


【解决方案1】:

根据Eric Lippert blogspecification,foreach 循环是一个语法糖:

{
  IEnumerator<string> e = ((IEnumerable<string>)new string[] { "pass1", "pass2", "pass3" }).GetEnumerator();
   try
   { 
     string pass; // OUTSIDE THE ACTUAL LOOP
      while(e.MoveNext())
      {
        pass = (string)e.Current;
        x = pass;
      }
   }
   finally
   { 
      if (e != null) ((IDisposable)e).Dispose();
   }
}

如你所见,枚举器是在循环之前创建的。

@Rawling 正确指出,编译器对该数组的处理略有不同。 Foreach 循环被优化为带有数组的 for 循环。根据The Internals of C# foreach,您的 C# 5 代码将如下所示:

string[] tempArray;
string[] array = new string[] { "pass1", "pass2", "pass3" };
tempArray = array;

for (string counter = 0; counter < tempArray.Length; counter++)
{
    string pass = tempArray[counter];
    x = pass;
}

初始化也只发生一次。

【讨论】:

  • +1 从 C# 5.0 开始,变量 pass 将在循环内。
  • 我认为这对于数组来说有些微妙的不同......它不是将foreach 循环变成for 循环吗?
  • @lazy 老实说,你的第一个代码块是最好的答案,因为它是规范所说的 (8.8.4) - 数组的 for 循环只是一点优化。跨度>
  • 我尽力了 :) (不要总是成功...)
  • 谢谢 - 问题已回答。
【解决方案2】:

如果你查看 ILSpy,这段代码会被翻译成类似

string[] array = new string[]
{
    "pass1",
    "pass2",
    "pass3"
};
for (int i = 0; i < array.Length; i++)
{
    string pass = array[i];
}

是的,数组只创建一次。

但是,说服您的同事的最佳参考可能是 C# 规范的第 8.8.4 节,它基本上会告诉您 LazyBerezovsky 的答案的作用。

【讨论】:

  • +1 感谢您指出不同的 foreach 数组编译!
【解决方案3】:

最初只创建一次。

我尝试了 Ofer Zelig 的建议(来自 cmets)

foreach (DateTime pass in new DateTime[] { DateTime.Now, DateTime.Now, DateTime.Now })
{
    int x = pass.Second; //etc
}

并放置了一个断点。即使您在迭代之间等待,它也会为所有 3 次迭代提供相同的秒数。

【讨论】:

  • 确保代码的好方法:) 我建议使用new[] { DateTime.Now, DateTime.Now, DateTime.Now } 使代码看起来更短:)
【解决方案4】:

您可以对其进行测试(有很多方法,但这是一种选择):

string pass4 = "pass4";
foreach (string pass in new string[] { "pass1", "pass2", "pass3", pass4 })
{
    pass4="pass5 - oops";
    x = pass; //etc
}

然后看看结果如何。

你会发现你是对的 - 它只执行了一次。

【讨论】:

  • 谢谢 - 问题已回答。
【解决方案5】:

下面的示例应该回答是否重新创建数组的问题。

        int i = 0;
        int last = 0;

        foreach (int pass in new int[] { i++, i++, i++, i++, i++, i++, i++ })
        {
            if (pass != last)
            {
                throw new Exception("Array is reintialized!");
            }
            last++;
        }

        if (i > 7)
        {
            throw new Exception("Array is reintialized!");
        }

【讨论】:

    猜你喜欢
    • 2011-01-25
    • 1970-01-01
    • 2013-05-15
    • 1970-01-01
    • 2014-06-04
    • 1970-01-01
    • 2019-07-10
    相关资源
    最近更新 更多