Only a member of the BCL team can tell us for sure, but it was probably just an oversight that List<T>.ForEach
lets you modify the list.
First, David B's answer doesn't make sense to me. It's List<T>
, not C#, that checks if you modify the list within a foreach
loop and throws an InvalidOperationException
if you do. It has nothing to do with the language you're using.
Second, there's this warning in the documentation:
Modifying the underlying collection in the body of the Action<T> delegate is not supported and causes undefined behavior.
I find it unlikely that the BCL team wanted such a simple method like ForEach
to have undefined behavior.
Third, as of .NET 4.5, List<T>.ForEach
will throw an InvalidOperationException
if the delegate modifies the list. If a program depends on the old behavior, it will stop working when it's recompiled to target .NET 4.5. The fact that Microsoft is willing to accept this breaking change strongly suggests that the original behavior was unintended and should not be relied upon.
For reference, here's how List<T>.ForEach
is implemented in .NET 4.0, straight from the reference source:
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]);
}
}
And here's how it's been changed in .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);
}