问题
In C#, I have noticed that if I am running a foreach loop on a LINQ generated IEnumerable<T>
collection and try to modify the contents of each T element, my modifications are not persistent.
On the other hand, if I apply the ToArray()
or ToList()
method when creating my collection, modification of the individual elements in the foreach loop are persistent.
I suspect that this is in some way related to deferred execution, but exactly how is not entirely obvious to me. I would really appreciate an explanation to this difference in behavior.
Here is some example code - I have a class MyClass
with a constructor and auto-implemented property:
public class MyClass
{
public MyClass(int val) { Str = val.ToString(); }
public string Str { get; set; }
}
In my example application I use LINQ Select()
to create two collections of MyClass
objects based on a collection of integers, one IEnumerable<MyClass>
, and one IList<MyClass>
by applying the ToList()
method in 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();
Next, I run a foreach loop over each of the collections, and modify the contents of the looped-over MyClass
objects:
foreach (var obj in myClassEnumerable) obj.Str = "Something";
foreach (var obj in myClassArray) obj.Str = "Something else";
Finally, I output the Str
member of the first element in each collection:
Console.WriteLine(myClassEnumerable.First().Str);
Console.WriteLine(myClassArray.First().Str);
Somewhat counter-intuitively, the output is:
1
Something else
回答1:
Deferred execution is the indeed the key point.
Executing myClassEnumerable.First().Str
will reexecute your query ints.Select(i => new MyClass(i));
and so it will give you a new IEnumerable with a new list of integers.
You can see this in action using your debugger. Put a breakpoint at the new MyClass(i)
part of the IEnumerable select and you will see that this part get's hit again when you execute it for Console.WriteLine
回答2:
You are right, it is deferred execution. A new MyClass instance is created each time you iterate the IEnumerable. By calling ToList or ToArray you then create a List or Array and populate it with the new MyClass instances created from the iteration of the IEnumerable.
来源:https://stackoverflow.com/questions/8064395/c-sharp-foreach-on-ienumerable-vs-list-element-modification-persistent-only-f