haughtonomous
haughtonomous

Reputation: 4850

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.

Upvotes: 16

Views: 16335

Answers (5)

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236328

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.

Upvotes: 27

Rawling
Rawling

Reputation: 50194

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.

Upvotes: 5

Erik Alsmyr
Erik Alsmyr

Reputation: 127

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

Upvotes: 1

Y2theZ
Y2theZ

Reputation: 10412

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.

Upvotes: 2

Jon Egerton
Jon Egerton

Reputation: 41579

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.

Upvotes: 1

Related Questions