12

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

如果您出于某种原因必须改变集合,那么您肯定应该在循环中手动迭代集合for吗?

foreach和之间似乎存在一些语义矛盾List<T>.ForEach()

我错过了什么吗?

4

3 回答 3

5

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

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

这意味着Action在元素上执行,可能会改变元素或集合本身。在这种情况下,没有其他方法(如果不创建昂贵的克隆集合,如果可能的话)负担得起,然后使用简单的for.

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

于 2012-07-15T15:57:42.047 回答
5

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

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

这是“语言与框架”混淆的一个例子。框架方法必须适用于多种语言,并且这些语言(通常)具有相互矛盾的语义。

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

于 2012-07-15T16:00:09.710 回答
5

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

首先,大卫 B 的回答对我来说没有意义。它List<T>不是 C#,而是检查您是否在循环中修改列表,如果这样做则foreach抛出一个。InvalidOperationException它与您使用的语言无关。

其次,文档中有这个警告:

不支持修改 Action<T> 委托主体中的基础集合,这会导致未定义的行为。

我发现 BCL 团队不太可能想要这样一种简单的方法,比如ForEach具有未定义的行为。

第三,从 .NET 4.5 开始,如果委托修改列表,List<T>.ForEach 抛出一个。InvalidOperationException如果程序依赖于旧行为,则在重新编译为目标 .NET 4.5 时它将停止工作。Microsoft 愿意接受这一重大更改的事实强烈表明最初的行为是无意的,不应依赖。

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

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);
}
于 2013-05-04T15:20:15.777 回答