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

Dav*_*New 12 .net c# foreach list

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

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

这里foreach和之间似乎有一点语义矛盾List<T>.ForEach().

我错过了什么吗?

Tig*_*ran 5

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

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

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

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

  • @davenewza不,链接的文件说_Modificare la raccolta sottostante nel corpoi'Action <T>`il delegatononèsupportatoe non [?] causa un comportamento indefinito._或者,如果由于某种原因更喜欢英语:_Modifying under underlying不支持`Action <T>`委托体中的集合并导致未定义的行为.所以他们说你不应该修改`action`委托中的`List <>`. (2认同)

Amy*_*y B 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语义.


Mic*_*Liu 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]);
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是 .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);
}
Run Code Online (Sandbox Code Playgroud)