• 系统报:Collection was modified; enumeration operation may not execute错误
  • 发布于 2个月前
  • 561 热度
    0 评论
  • 苒青绾
  • 0 粉丝 28 篇博客
  •   
本周继续偷懒打个支线任务。图片这是一个算是比较古老的东西,但是实际工作中可能并不会想到。对于List,使用Foreach获取里面的数据是非常普遍的操作。如果我们在循环中改变List,会怎么样?
于是,我们运行下面这个代码。
static void Main(string[] args)
{
            List<int> list = new List<int>() { 0,1,2,3,4,5,6,7,8,9};
            list.ForEach(l => 
            {   
                Console.WriteLine(l);
                if(l == 5)
                {
                    list.RemoveAt(0);
                }
            });
        }
然后,引发了一个异常:
System.InvalidOperationException:“Collection was modified; enumeration operation may not execute.”

这是为什么呢,显然Foreach这个方法里面有点东西。
在List中,Foreach的方法代码如下:
[__DynamicallyInvokable]
public void ForEach(Action<T> action)
{
  if (action == null)
  {
    ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
  }
  int version = this._version;
  int num = 0;
  while (num < this._size && (version == this._version || !BinaryCompatibility.TargetsAtLeast_Desktop_V4_5))
  {
    action(this._items[num]);
    num++;
  }
  if (version != this._version && BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
  {
    ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
  }
}
里面关注这两个部分,这个异常是下面的if判断抛出来的。TargetsAtLeast_Desktop_V4_5这个不用太过关心,这年头了,版本比这个低,你该想想自己的问题。

那么就是说两个version不相等了。Foreach初始化的时候是相同的,那么是哪里改变的呢?我们回到自己的代码,在循环里面,我们进行了Remove。
list.RemoveAt(0);
我们看下RemoveAt的代码
[__DynamicallyInvokable]
    public void RemoveAt(int index)
    {
      if (index >= this._size)
      {
        ThrowHelper.ThrowArgumentOutOfRangeException();
      }
      this._size--;
      if (index < this._size)
      {
        Array.Copy(this._items, index + 1, this._items, index, this._size - index);
      }
      this._items[this._size] = default(T);
      this._version++;
    }
最后这里,看到了吗,当我们修改了List,Version就会递增。
this._version++;
包括但不限于Remove系列方法,Clear,Insert等一系列修改List的操作,都会导致version改变,从而Foreach报错。
用户评论