Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I set properties of iteration variables in a foreach loop?

Tags:

c#

foreach

linq

string newName = "new name";

int[] numbers = new int[] { 1, 2, 3 };

var people = numbers.Select(n => new Person()
{
    Name = n.ToString()
});

foreach (var person in people)
{
    person.Name = newName;
}

Debug.WriteLine(people.First().Name == newName); // returns false

I expected the above line to return true. Why can't I set properties of iteration variables inside a foreach loop?

like image 635
Yeonho Avatar asked Feb 03 '12 02:02

Yeonho


2 Answers

people is a query definition with deferred execution. Your foreach over the query is irrelevant, this isn't about an inability to set a property. When you invoke First(), you run the query again.

To be clear, the query definition here is that for the elements in numbers, create a new Person and assign the value of the current number element to the Person's Name property. When you iterate in the foreach loop, the query evaluates, and you create new Person objects. But those Person objects are not in the query, it's just a definition! Running the query again executes the definition again, creating different Person objects. The fact that you modified the query's original results does not impact the second set of results.

If you would like an immediate execution, use

var people = numbers.Select(n => new Person() 
    { 
        Name = n.ToString() 
    }).ToList(); 

You'll find your changes in the loop sticking, because now people is a concrete list instead of a query definition.

foreach (var person in people) 
{ 
     person.Name = newName; 
} 

Debug.WriteLine(people.First().Name.Equals(newName)); // returns true
like image 146
Anthony Pegram Avatar answered Oct 10 '22 10:10

Anthony Pegram


This is a perfect example of deferred execution.

If you try this example it works as you expect since the ToList executes the query.

    string newName = "new name";

    int[] numbers = new int[] { 1, 2, 3 };

    var people = numbers.Select(n => new Person()
    {
        Name = n.ToString()
    }).ToList(); // <===== here

    foreach (var person in people)
    {
        person.Name = newName;
    }

    var b = people.First().Name == newName;
like image 20
Lester Avatar answered Oct 10 '22 10:10

Lester