【问题标题】:C# foreach on IEnumerable vs. List - element modification persistent only for array - Why?IEnumerable 与 List 上的 C# foreach - 元素修改仅适用于数组 - 为什么?
【发布时间】:2011-11-09 11:32:45
【问题描述】:

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

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

我怀疑这在某种程度上与延迟执行有关,但具体如何对我来说并不完全清楚。我真的很感激对这种行为差异的解释。

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

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

在我的示例应用程序中,我使用 LINQ Select() 根据整数集合创建两个 MyClass 对象集合,一个 IEnumerable<MyClass> 和一个 IList<MyClass>,最后应用 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

【问题讨论】:

  • 编辑了帖子以保持一致性。 FWIW,使用MyClass[] arr = enumerable.ToArray() 时也是如此。现在为了真正的乐趣,请尝试使用 struct MyStruct { ... } 代替 :)
  • 谢谢,谢。是的,我有点猜到 struct 无论如何都不是一个选项。

标签: c# foreach reference ienumerable


【解决方案1】:

延迟执行确实是关键。

执行myClassEnumerable.First().Str 将重新执行您的查询ints.Select(i => new MyClass(i));,因此它将为您提供一个带有新整数列表的新IEnumerable。

您可以使用调试器看到这一点。在 IEnumerable 选择的 new MyClass(i) 部分放置一个断点,当您为 Console.WriteLine 执行它时,您会看到该部分再次被命中

【讨论】:

    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-07
      • 2015-09-08
      • 2015-11-19
      • 2019-07-01
      • 2017-05-05
      相关资源
      最近更新 更多