【问题标题】:Why is foreach loop Read-Only in C#为什么foreach循环在C#中是只读的
【发布时间】:2011-04-29 14:50:58
【问题描述】:

为什么 foreach 循环是只读的?我的意思是您可以获取数据但不能增加++或减少--。背后有什么原因吗? 是的,我是初学者:)

示例:

int[] myArray={1,2,3};
foreach (int num in myArray)
{
  num+=1;
}

【问题讨论】:

  • 我认为他在谈论迭代变量,如在 foreach 中( Item theItem in theItemList )... theItem 无法分配给。
  • 是的,我说的是 foreach 循环。自然是循环吧?在 for 循环中,我们可以遍历一个数组并同时更改数组中的值,但 foreach 不起作用。
  • @Zai:好吧,严格来说不是这样;请参阅下面的答案。
  • 我认为更有趣的是,您也无法从foreach 中添加或删除集合。

标签: c# loops


【解决方案1】:

这是因为 foreach 旨在遍历容器,确保每个项目只被访问一次,而不更改容器,以避免讨厌的副作用。

见:foreach in MSDN

如果您的意思是为什么像整数这样的元素的更改不会影响整数容器,这是因为在这种情况下迭代的变量将是一个值类型并且被复制,例如:

// Warning: Does not compile
foreach (int i in ints)
{
  ++i; // Would not change the int in ints
}

即使迭代的变量是引用类型,其操作返回一个新对象,您也不会更改原始集合,大部分时间您只是重新分配给该变量:

// Warning: Does not compile
foreach (MyClass ob in objs)
{
  ob=ob+ob; // Reassigning to local ob, not changing the one from the original 
            // collection of objs
}

以下示例有可能通过调用 mutating 方法实际修改原始集合中的对象:

// Warning: Does not compile
foreach (MyClass ob in objs)
{
  ob.ChangeMe(); // This could modify the object in the original collection
}

为避免混淆值与引用类型以及上述场景(以及与优化相关的一些原因),MS 选择将迭代变量设为readonly

【讨论】:

  • 当您说// This could modify the object in the original collection 时,只有当集合不是纯IEnumerable 时,即集合或列表,对吧?
【解决方案2】:

因为当前元素是按值返回的(即复制的)。而且修改副本是没有用的。如果是引用类型,则可以修改该对象的内容,但不能替换引用。

也许你应该阅读IEnumerable<T>IEnumerator<T> 的文档。那应该更清楚。最重要的一点是IEnumerable<T> 具有T 类型的属性Current。而且这个属性只有一个getter,没有setter。

但是如果它有一个 setter 会发生什么?

  • 它适用于数组和列表
  • 它不适用于哈希表、有序列表等复杂容器,因为更改会导致容器发生较大变化(例如重新排序),从而使迭代器无效。 (大多数集合在修改迭代器以避免迭代器中的状态不一致时会使它们失效。)
  • 在 LINQ 中它完全没有意义。例如,select(x=>f(x)) 的值是函数的结果,没有关联的永久存储。
  • 使用 yield return 语法编写的迭代器也没有任何意义

【讨论】:

  • "而且修改副本也没用。"如果我想做一些事情,比如对列表中所有数字的平方求和怎么办?可以通过只读限制来完成,但修改副本通常很有用。
  • +1 表示“最重要的一点是 IEnumerable 有一个 T 类型的 Current 属性。这个属性只有一个 getter,但没有 setter。”终于有一个彻底解释它的答案了。
  • 但是如果你可以更新迭代变量所引用的对象的属性,难道这也不能改变有序列表中的顺序吗?
【解决方案3】:

foreach 被设计为只访问集合中的每个项目一次,并且不使用显式的“循环索引”;如果您想更好地控制循环并拥有循环索引,请使用for

编辑:您可以在 foreach 循环内更改正在迭代的集合中的项目。例如:

foreach(Chair ch in mychairs)
{
    ch.PaintColour = Colour.Green; //this alters the chair object *in* the collection.
}

但是,您不能在集合中添加或删除项目。

【讨论】:

  • 其实也不能修改迭代变量所引用的对象的字段/属性。
  • @Michael:我发布的代码对我来说没问题。我正在使用 .NET 3.5 和 Visual Studio 2008。
  • @MichaelGoldshteyn 只有迭代变量是只读的,而不是对象被引用。
猜你喜欢
  • 1970-01-01
  • 2011-03-18
  • 1970-01-01
  • 2013-11-06
  • 1970-01-01
  • 2013-01-04
  • 2010-11-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多