16

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.

10
  • 10
    You can easily test it, put a DateTime value for example instead of strings and inspect the members in each iteration. Commented Feb 27, 2013 at 14:23
  • Another way to show them it's only evaluated once is to step through the code in debug mode. The code that creates the string array will only be highlighted once. Commented Feb 27, 2013 at 14:24
  • or new string[] { "pass1", "pass" + i++, "pass3" } Commented Feb 27, 2013 at 14:25
  • 5
    "a mysterious bug that reports that a collection has been changed whilst iterating over it" - why not post THAT as the question? Commented Feb 27, 2013 at 14:28
  • 3
    "a mysterious bug that reports that a collection has been changed whilst iterating over it." - this suggests you have code that is removing items from a collection inside of a loop. that's what you should be hunting for Commented Feb 27, 2013 at 14:28

5 Answers 5

27

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.

Sign up to request clarification or add additional context in comments.

12 Comments

+1 Starting from C# 5.0, the variable pass would be inside the loop.
I think this is subtly different for an array... doesn't it turn a foreach loop into a for loop?
@lazy To be honest, your first code block is the best answer because it's what the spec says (8.8.4) - the for loop for arrays is just a bit of optimisation.
I try my best :) (Don't always succeed...)
Thanks - question answered.
|
5

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.

1 Comment

+1 and thanks for pointing on different foreach compiling for arrays!
2

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.

1 Comment

Nice way to ensure :) I recommend using new[] { DateTime.Now, DateTime.Now, DateTime.Now } to make code look shorter :)
1

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.

1 Comment

Thanks - question answered.
1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.