Why do I have to create a concrete implementation of IEnumerable<T> in order to modify its members in the foreach loop?
This blog post (Exhibit 1) explains the behavior, but I can’t quite wrap my head around it.
I’ve got a very simple code snippet here to reproduce the issue (C# 4.0 / .NET 4.0).
class Person
{
public int Age { get; set; }
public Person()
{
}
}
class Program
{
static void Main(string[] args)
{
//calling .ToList() on GetPeople() below will fix the issue
var people = GetPeople();
foreach (var item in people)
{
item.Age = DateTime.Now.Second;
}
foreach (var item in people)
{
Console.WriteLine("Age is {0}", item.Age);
}
Console.Read();
}
public static IEnumerable<Person> GetPeople()
{
int i = 0;
while (i < 3)
{
i++;
yield return new Person();
}
}
}
Each time you iterate over
people, it will execute the code inGetPeople()again – creating new instances ofPerson. The code inGetPeopledoes not run when you callGetPeople(); it only starts running when you call something:… which is what the
foreachloop does.If you call
ToList(), that means you only execute the code inGetPeople()once, and store the references returned when iterating over the sequence. At that point, each time you iterator over theList<Person>you’ll iterate over references to the same objects, so any modifications you make in one loop will be seen in another.You may find it slightly easier to understand what’s going on if you put logging (or a breakpoint) in
GetPeople(). I have an article which goes into the implementation details, which may make things clearer too.