为什么List <t> .ForEach()实现for循环?</t>

时间:2012-07-15 15:52:30

标签: c# .net list foreach

我不明白为什么List<T>.ForEach()扩展方法会在引擎盖下实现for循环。这开辟了收集被修改的可能性。正常foreach会在这种情况下抛出异常,所以ForEach()肯定会以同样的方式做出反应吗?

如果你因任何原因必须改变一个集合,那么你肯定应该在for循环中手动迭代集合吗?

foreachList<T>.ForEach()之间似乎存在一些语义矛盾。

我错过了什么吗?

3 个答案:

答案 0 :(得分:5)

因为List.ForEach遵循MSDN中的定义:

  

对List的每个元素执行指定的操作。

这意味着对元素执行的Action可能会更改元素,或集合本身。在这种情况下,没有其他方法(如果没有创建costy克隆集合,如果可能的话)支付这个,然后使用简单的for

如果在foreach的迭代过程中更改了集合,它自然会引发异常。

答案 1 :(得分:5)

foreach是一个C#语言元素。它遵循C#的规则。

List<T>.ForEach是一种.NET Framework方法。它遵循.NET规则,其中foreach不存在。

这是“语言与框架”混淆的一个例子。框架方法必须以多种语言工作,语言(通常)具有矛盾的语义。

这种“语言与框架”混淆的另一个例子是.net 3和.NET 3.5之间对Enumerable.Cast的重大改变。在.NET 3中,Cast使用了C#语义。在.net 3.5中,它被改为使用.net语义。

答案 2 :(得分:4)

只有BCL团队的成员可以肯定地告诉我们,但这可能仅仅是List<T>.ForEach允许您修改列表的疏忽。

首先,David B的答案对我没有意义。它是List<T>,而不是C#,它会检查您是否在foreach循环内修改了列表,如果这样做,则会抛出InvalidOperationException。它与你正在使用的语言无关。

其次,documentation

中有此警告
  

修改Action&lt; T&gt;正文中的基础集合。委托不受支持并导致未定义的行为。

我发现BCL团队不太可能希望像ForEach这样的简单方法具有未定义的行为。

第三,从.NET 4.5开始,如果委托修改了列表,List<T>.ForEach 抛出InvalidOperationException。如果程序依赖于旧行为,it will stop working何时将其重新编译为目标.NET 4.5。微软愿意接受这一突破性变化的事实强烈表明,原始行为是无意的,不应该依赖。

供参考,以下是直接来自参考源的.NET 4.0中List<T>.ForEach的实现方式:

public void ForEach(Action<T> action) {
    if( action == null) {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    Contract.EndContractBlock();

    for(int i = 0 ; i < _size; i++) {
        action(_items[i]);
    }
}

以下是.NET 4.5中的更改方式:

public void ForEach(Action<T> action) {
    if( action == null) {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    Contract.EndContractBlock();

    int version = _version;

    for(int i = 0 ; i < _size; i++) {
        if (version != _version && BinaryCompatibility.TargetsAtLeast_Desktop_V4_5) {
            break;
        }
        action(_items[i]);
    }

    if (version != _version && BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
        ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
}