简体   繁体   中英

C# loops: iterating through an array

If I have a loop such as below:

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

does the anonymous string array get created once initially, or recreated once for each pass?

I believe the former, but collegues are convinced this is a bug waiting to happen because they say every iteration of the foreach loop results in a new string array being created.

The VS Disassembly code suggests I am right, but I want to be sure.

The reason we are looking at this is to try to understand a mysterious bug that reports that a collection has been changed whilst iterating over it.

According to Eric Lippert blog and specification , foreach loop is a syntactic sugar for:

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

As you can see, enumerator is created before loop.

@Rawling correctly pointed, that array treated a little different by compiler. Foreach loop is optimized into for loop with arrays. According to The Internals of C# foreach your code for C# 5 will look like:

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

Initialization also happens only once.

If you look in ILSpy, this code is translated into something like

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

so yes, the array is only created once.

However, the best reference to convince your colleagues is probably section 8.8.4 of the C# specification, which will tell you essentially what LazyBerezovsky's answer does.

It is created only once initially.

I tried the suggestion by Ofer Zelig (from the comments)

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

And placed a breakpoint. It will give the same seconds for all 3 iterations even if you wait between iterations.

You could test it (plenty of ways to do so, but this is one option):

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

Then see what comes out.

You'll find you're right - its only executed the one time.

The example below should answer the question if the array is recreated or not.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM