如何在foreach循环期间阻止修改IEnumerable <T>元素

lys*_*cid -3 .net c# clr

众所周知,在C#中迭代一些IEnumerable时,不能对可枚举集合的元素进行修改:

// Illegal code
foreach (Employee e in employeeList)
{
     e.Salary = 1000000;
}
Run Code Online (Sandbox Code Playgroud)

我想知道运行时或枚举器本身是如何强制执行的?

Mic*_*eld 6

我认为你误解了限制.你发布的代码是完全合法的,事实上,如果不是那么foreach循环将是非常无用的.

不允许的是在循环中间更改序列foreach:

foreach ( Employee e in employeeList )
{
  if ( e.Salary > 10000000 ) 
  {
    employeeList.Remove(e);
  }
}
Run Code Online (Sandbox Code Playgroud)

此代码InvalidOperationException在运行时抛出:执行中的调用将MoveNextIEnumerator检测到底层集合在调用之间发生更改时抛出.这是实现枚举器对象的要求 - 每个实现IEnumerable都必须为自己处理这种可能性.请注意,某些集合(来自.NET 4.0的新并发集合)明确地不强制执行此限制,因此上面的代码是合法的employeeList,例如,a ConcurrentDictionary.有关详细信息,请参阅此博文.

为循环控制变量赋值也是不合法的,例如:

foreach ( Employee e in employeeList )
{
  if ( e.Salary > 10000000 ) 
  {
    e = new Employee { Salary = 999999 };
  }
}
Run Code Online (Sandbox Code Playgroud)

这是不允许的原因是因为它没有多大意义,几乎可以肯定是一个bug; 有关详细信息,请参阅此答案:为什么C#foreach语句中的迭代变量是只读的?

请注意,这并不是要尝试更改序列中的元素- 它只是尝试更改用作循环控制变量的局部变量的值.此外,枚举器运行时也不强制执行此操作.这是一个编译时错误,由C#编译器强制执行.