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

发布于 2024-12-14 06:04:45 字数 1368 浏览 5 评论 0原文

在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

痴骨ら 2024-12-21 06:04:45

延迟执行确实是关键点。

执行 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 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

寻找我们的幸福 2024-12-21 06:04:45

你说得对,就是延期执行。每次迭代 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文