为什么我不能在foreach中修改循环变量?

时间:2010-08-23 21:05:17

标签: c# foreach

为什么foreach循环是只读循环?有什么理由呢?

6 个答案:

答案 0 :(得分:22)

我不确定你的“readonly循环”是什么意思,但我猜你想知道为什么不能编译:

int[] ints = { 1, 2, 3 };
foreach (int x in ints)
{
    x = 4;
}

上面的代码将给出以下编译错误:

Cannot assign to 'x' because it is a 'foreach iteration variable'

为什么不允许这样做?试图分配它可能不会做你想要的 - 它不会修改原始集合的内容。这是因为变量x不是对列表中元素的引用 - 它是一个副本。为了避免人们编写错误的代码,编译器不允许这样做。

答案 1 :(得分:5)

我会假设它是迭代器在列表中传播的方式。

假设您有一个排序列表:

Alaska
Nebraska
Ohio

的中间
foreach(var s in States)
{
}

你做了States.Add(“Missouri”)

你是如何处理的?如果你已经超过了索引,那么你是否会跳到密苏里州。

答案 2 :(得分:3)

如果,你的意思是:

  

为什么我不应该修改foreach过来的集合?

无法保证您获得的物品按照给定的顺序出现,添加物品或移除物品不会导致集合中物品的顺序发生变化,甚至不会导致枚举器变为变得无效。

想象一下,如果您运行以下代码:

var items = GetListOfTOfSomething(); // Returns 10 items

int i = 0;
foreach(vat item in items)
{
    i++;
    if (i == 5)
    {
        items.Remove(item);
    }
}

一旦你点击我是6的循环(即在删除项目之后),就会发生任何事情。由于您删除了一个项目,枚举器可能已经失效,所有内容可能已经在底层集合中“拖拽一个”,导致项目取代已删除的项目,这意味着您“跳过”一个项目。

如果您的意思是“为什么我不能更改每次迭代提供的值”,那么,如果您正在使用的集合包含value types,那么您所做的任何更改将不会保留,因为它是您正在使用的值,而不是引用

答案 3 :(得分:1)

foreach命令使用IEnumerable接口循环遍历集合。接口只定义了单步调试集合并获取当前项目的方法,没有更新集合的方法。

由于界面仅定义了在一个方向上读取collecton所需的最小方法,因此该接口可以通过各种集合实现。

由于您一次只能访问一个项目,因此整个集合不必同时存在。例如,它由LINQ表达式使用,它在您读取时动态创建结果,而不是首先创建整个结果,然后让您遍历它。

答案 4 :(得分:1)

不确定你的只读是什么意思,但我猜想了解foreach循环的内涵会有所帮助。它是语法糖,也可以写成这样的东西:

IEnumerator enumerator = list.GetEnumerator();
while(enumerator.MoveNext())
{
   T element = enumerator.Current;
   //body goes here
}

如果更改集合(列表),很难弄清楚如何处理迭代。 分配给元素(在foreach版本中)可以被视为尝试分配给只读的enumerator.Current或者尝试更改本地的值,将ref保存为枚举器。当然,您也可以介绍一个本地人,因为它不再与枚举列表有任何关系。

答案 5 :(得分:0)

foreach适用于实现IEnumerable接口的所有内容。为了避免同步问题,在迭代时永远不会修改枚举。

如果在迭代时在另一个线程中添加或删除项目,则会出现问题:根据您的位置,您可能会错过某个项目或将您的代码应用于额外的项目。这由运行时检测到(在某些情况下或全部???)并抛出异常:

System.InvalidOperationException was unhandled
  Message="Collection was modified; enumeration operation may not execute."

foreach尝试在每次迭代时获取下一个项目,如果您同时从另一个线程修改它,可能会造成麻烦。