Reputation: 4850
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
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
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
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
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
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