显然有许多方法可以迭代集合。很好奇是否有什么不同,或者为什么你用一种方式而不是另一种。

第一类型:

List<string> someList = <some way to init>
foreach(string s in someList) {
   <process the string>
}

其他方式:

List<string> someList = <some way to init>
someList.ForEach(delegate(string s) {
    <process the string>
});

我想,除了我上面使用的匿名委托,你还可以指定一个可重用的委托。


当前回答

您展示的第二种方法使用扩展方法为列表中的每个元素执行委托方法。

这样,您就有了另一个委托(=方法)调用。

此外,还可以使用for循环迭代列表。

其他回答

在幕后,匿名委托被转换为一个实际的方法,因此如果编译器没有选择内联函数,那么第二个选择可能会有一些开销。此外,匿名委托示例主体引用的任何局部变量在本质上都会发生变化,因为编译器会使用一些技巧来隐藏它被编译为新方法的事实。这里有更多关于c#如何做到这一点的信息:

http://blogs.msdn.com/oldnewthing/archive/2006/08/04/688527.aspx

这两者之间有一个重要而有用的区别。

因为.ForEach使用for循环来迭代集合,这是有效的(编辑:在。net 4.5之前-实现改变了,它们都抛出):

someList.ForEach(x => { if(x.RemoveMe) someList.Remove(x); }); 

而foreach使用枚举数,因此这是无效的:

foreach(var item in someList)
  if(item.RemoveMe) someList.Remove(item);

不要复制这段代码到你的应用程序中!

这些示例并不是最佳实践,它们只是为了演示ForEach()和ForEach之间的区别。

在for循环中从列表中删除项可能会产生副作用。最常见的是在这个问题的评论中描述的。

通常,如果希望从列表中删除多个项,则需要将确定要删除哪些项与实际删除分开。它不能使您的代码保持紧凑,但它保证您不会遗漏任何项。

您展示的第二种方法使用扩展方法为列表中的每个元素执行委托方法。

这样,您就有了另一个委托(=方法)调用。

此外,还可以使用for循环迭代列表。

整个ForEach作用域(委托函数)被视为一行代码(调用该函数),您不能设置断点或进入代码。如果发生未处理的异常,则标记整个块。

为了好玩,我将List弹出到reflector中,结果是c#:

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]);
    }
}

类似地,foreach使用的枚举器中的MoveNext是这样的:

public bool MoveNext()
{
    if (this.version != this.list._version)
    {
        ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
    }
    if (this.index < this.list._size)
    {
        this.current = this.list._items[this.index];
        this.index++;
        return true;
    }
    this.index = this.list._size + 1;
    this.current = default(T);
    return false;
}

列表中。ForEach比MoveNext更精简——处理更少——将更有可能JIT成高效的东西。

此外,foreach()将分配一个新的Enumerator。GC是你的朋友,但如果你重复做同样的foreach,这将产生更多的一次性对象,而不是重用同一个委托- but -这确实是一个边缘情况。在典型用法中,您将看到很少或没有区别。