我们正在查看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]);
}
答案 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的调用。