为什么List <t> .ForEach在循环结束后检查版本?

时间:2016-01-05 09:19:23

标签: c# list foreach

我们正在查看List.ForEach method的源代码,其实现如下:

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

我们找不到两次检查版本的原因。我们认为可以抛出异常而不是break。我们还认为,如果在循环结束后,在执行检查之前由另一个Thread修改了列表,则最终检查可能会导致不必要的异常。

为什么循环没有这样实现:

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

1 个答案:

答案 0 :(得分:4)

该检查基本上验证了对操作委托的最后一次调用是否不会更改集合。

由于这是合同的一部分,因此remarks in the documentation

  

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

然后这是完全合法的,也是正确的事情。

此外,他们已经将循环内部异常的抛出推迟到循环之后的代码,以避免重复那些有意义的代码。

现在,说完了,代码可以写成:

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

代码的当前外观可能是由反编译或类似因素造成的,它会复制if-part而不是对ThrowHelper的调用。