16

以下のようなループがある場合:

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

匿名の文字列配列は、最初に 1 回作成されますか、それともパスごとに 1 回再作成されますか?

私は前者だと信じていますが、同僚は、foreach ループの反復ごとに新しい文字列配列が作成されると言っているため、これは発生するのを待っているバグであると確信しています。

VS 逆アセンブリ コードは、私が正しいことを示唆していますが、確認したいと思います。

これを調べている理由は、反復処理中にコレクションが変更されたことを報告する謎のバグを理解しようとするためです。

4

5 に答える 5

27

Eric Lippert のブログ仕様によると、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;
}

初期化も一度だけ行われます。

于 2013-02-27T14:26:18.133 に答える
5

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 の答えが何をするかを本質的に教えてくれます。

于 2013-02-27T14:32:37.987 に答える
2

最初に一度だけ作成されます。

Ofer Zeligによる提案を試しました(コメントから)

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

そして、ブレークポイントを配置しました。反復間で待機しても、3 つの反復すべてで同じ秒数が得られます。

于 2013-02-27T14:30:31.923 に答える
1

あなたはそれをテストすることができます(そうする方法はたくさんありますが、これは1つのオプションです):

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

次に、何が出るかを見てください。

あなたは正しいことがわかります-それは一度だけ実行されます。

于 2013-02-27T14:26:13.027 に答える
1

以下の例は、配列が再作成されるかどうかという質問に答える必要があります。

        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!");
        }
于 2013-02-27T14:41:24.190 に答える