C # foreach in IEnumerable vs. List - element modification is constant only for the array - why?

In C #, I noticed that if I run the foreach loop in the LINQ-generated IEnumerable<T> collection and try to change the contents of each T element, my modifications are not permanent.

On the other hand, if I use the ToArray() or ToList() method when creating my collection, changing individual elements in the foreach loop is permanent.

I suspect that this is somehow related to deferred execution, but just as it is not entirely obvious to me. I would really appreciate an explanation of this difference in behavior.

Here is a sample code - I have a MyClass class with a constructor and an automatically implemented property:

 public class MyClass { public MyClass(int val) { Str = val.ToString(); } public string Str { get; set; } } 

In my sample application, I use LINQ Select() to create two collections of MyClass objects based on a set of integers, one IEnumerable<MyClass> and one IList<MyClass> using the ToList() method at the end.

 var ints = Enumerable.Range(1, 10); var myClassEnumerable = ints.Select(i => new MyClass(i)); var myClassArray = ints.Select(i => new MyClass(i)).ToList(); 

Then I run a foreach loop on each of the collections and modify the contents of the objects with a circular MyClass :

 foreach (var obj in myClassEnumerable) obj.Str = "Something"; foreach (var obj in myClassArray) obj.Str = "Something else"; 

Finally, I infer the Str member from the first element in each collection:

 Console.WriteLine(myClassEnumerable.First().Str); Console.WriteLine(myClassArray.First().Str); 

Somewhat counter-intuitive, output:

 1 Something else 
+3
reference c # foreach ienumerable
Nov 09 '11 at 11:32
source share
2 answers

Delayed execution is indeed a key point.

Running myClassEnumerable.First().Str will cause your query to execute again ints.Select(i => new MyClass(i)); and it will provide you with a new IEnumerable with a new list of integers.

You can see this in action using your debugger. Place a breakpoint on the new MyClass(i) part of the IEnumerable element and you will see that this part will be deleted again when you run it for Console.WriteLine

+13
Nov 09 '11 at 11:35
source share

You are right, this is a delayed execution. A new instance of MyClass is created each time you repeat IEnumerable. By calling ToList or ToArray, you create a list or array and populate it with new MyClass instances created from the IEnumerable iteration.

+3
Nov 09 '11 at 11:39
source share



All Articles