C# foreach on IEnumerable vs. List - 元素修改仅对数组持久 - 为什么?
在 C# 中,我注意到,如果我在 LINQ 生成的 IEnumerable
集合上运行 foreach 循环并尝试修改每个 T 元素的内容,我的修改不会 em> 执着。
另一方面,如果我在创建集合时应用 ToArray()
或 ToList()
方法,则对 foreach 循环中各个元素的修改是< /em> 坚持不懈。
我怀疑这在某种程度上与延迟执行有关,但具体如何对我来说并不完全明显。我真的很感谢对这种行为差异的解释。
下面是一些示例代码 - 我有一个带有构造函数和自动实现属性的类 MyClass
:
public class MyClass
{
public MyClass(int val) { Str = val.ToString(); }
public string Str { get; set; }
}
在我的示例应用程序中,我使用 LINQ Select()
创建两个 MyClass
集合。 code>MyClass 对象基于整数集合、一个 IEnumerable
和一个 IList
,方法是应用最后是 ToList()
方法。
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
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
延迟执行确实是关键点。
执行
myClassEnumerable.First().Str
将重新执行您的查询ints.Select(i => new MyClass(i));
因此它会给您一个新的 IEnumerable带有新的整数列表。您可以使用调试器查看其实际情况。在 IEnumerable 选择的
new MyClass(i)
部分放置一个断点,当您为 Console.WriteLine 执行它时,您将看到该部分再次被命中Deferred execution is the indeed the key point.
Executing
myClassEnumerable.First().Str
will reexecute your queryints.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你说得对,就是延期执行。每次迭代 IEnumerable 时都会创建一个新的 MyClass 实例。通过调用 ToList 或 ToArray,您可以创建一个 List 或 Array,并使用从 IEnumerable 迭代创建的新 MyClass 实例填充它。
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.