为什么列出<;T>;.ForEach在循环结束后检查版本

本文关键字:结束 循环 版本 检查 gt lt 为什么 ForEach | 更新日期: 2023-09-27 18:28:57

我们查看了List.ForEach方法的源代码,它的实现如下:

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。我们还认为,如果在循环完成后,在执行检查之前,另一个线程修改了列表,那么最终检查可能会导致不必要的异常。

简单地说,为什么循环不是这样实现的:

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

为什么列出<;T>;.ForEach在循环结束后检查版本

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

由于这是合同的一部分,根据文件中的备注:

不支持修改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部分,而不是对ThrowHelper的调用。