C#foreach on IEnumerable vs. List – 元素修改仅对数组持久 – 为什么?

在C#中,我注意到如果我在LINQ生成的IEnumerable集合上运行foreach循环并尝试修改每个T元素的内容,那么我的修改不会持久。

另一方面,如果在创建集合时应用ToArray()ToList()方法,则对foreach循环各个元素的修改持久的。

我怀疑这在某种程度上与延迟执行有关,但究竟对我来说并不完全是显而易见的。 我真的很感激这种行为差异的解释。

这是一些示例代码 – 我有一个带有构造函数和自动实现属性的类MyClass

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

在我的示例应用程序中,我使用LINQ Select()通过最终应用ToList()方法,基于整数集合创建两个MyClass对象集合,一个IEnumerable和一个IList

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

接下来,我在每个集合上运行一个foreach循环,并修改循环的MyClass对象的内容:

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

最后,我输出每个集合中第一个元素的Str成员:

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

有点违反直觉,输出是:

 1 Something else 

延期执行确实是关键点。

执行myClassEnumerable.First().Str将重新执行你的查询ints.Select(i => new MyClass(i)); 所以它会给你一个新的IEnumerable和一个新的整数列表。

您可以使用调试器查看此操作。 在IEnumerable select的new MyClass(i)部分放置一个断点,当你为Console.WriteLine执行它时,你会看到这个部分再次被击中

你是对的,它是延期执行。 每次迭代IEnumerable时都会创建一个新的MyClass实例。 通过调用ToList或ToArray,然后创建一个List或Array,并使用从IEnumerable的迭代创建的新MyClass实例填充它。