为什么List <T> .ForEach允许修改其列表?

SWe*_*eko 90 c# foreach list

如果我使用:

var strings = new List<string> { "sample" };
foreach (string s in strings)
{
  Console.WriteLine(s);
  strings.Add(s + "!");
}
Run Code Online (Sandbox Code Playgroud)

Addforeach引发InvalidOperationException(集合被修改;枚举操作可能不会执行),我认为合理的,因为我们从我们脚下拉地毯.

但是,如果我使用:

var strings = new List<string> { "sample" };
strings.ForEach(s =>
  {
    Console.WriteLine(s);
    strings.Add(s + "!");
  });
Run Code Online (Sandbox Code Playgroud)

它通过循环迅速射入脚中直到它抛出OutOfMemoryException.

这对我来说是一个惊喜,因为我一直认为List.ForEach只是一个包装foreach或for for.
有没有人对这种行为的方式和原因有解释?

(通过FOREach 循环获取无限重复的通用列表)

Tho*_*que 68

这是因为该ForEach方法不使用枚举器,它循环遍历项for:

public void ForEach(Action<T> action)
{
    if (action == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    for (int i = 0; i < this._size; i++)
    {
        action(this._items[i]);
    }
}
Run Code Online (Sandbox Code Playgroud)

(使用JustDecompile获得的代码)

由于未使用枚举器,因此它永远不会检查列表是否已更改,并且for永远不会达到循环的结束条件,因为_size在每次迭代时都会增加.

  • 它在Add方法刷新 - > this._items [this._size ++] = item; (7认同)

Ale*_*aga 14

List<T>.ForEach是通过for内部实现的,所以它不使用枚举器,它允许修改集合.


Mik*_*oud 6

因为附加到List类的ForEach在内部使用直接连接到其内部成员的for循环 - 您可以通过下载.NET框架的源代码来查看.

http://referencesource.microsoft.com/netframework.aspx

其中foreach循环首先是编译器优化,但也必须作为观察者对集合进行操作 - 因此,如果集合被修改,则抛出异常.