I don't understand why the List<T>.ForEach()
extension method implements a for
loop under the hood. This opens up the possibility of the collection being modified. A normal foreach
will throw an exception in this case so surely ForEach()
should react the same way?
If you MUST mutate a collection for whatever reason, then surely you should be manually iterating through the collection in a for
loop?
There seems to be a bit of a semantic contradiction here between foreach
and List<T>.ForEach()
.
Am I missing something?
The foreach loop in C# iterates items in a collection, like an array or a list. It proves useful for traversing through each element in the collection and displaying them. The foreach loop is an easier and more readable alternative to for loop.
For-each cannot be used to initialize any array or Collection, because it loops over the current contents of the array or Collection, giving you each value one at a time. The variable in a for-each is not a proxy for an array or Collection reference.
The FOR loop without length caching and FOREACH work slightly faster on arrays than FOR with length caching. Array. Foreach performance is approximately 6 times slower than FOR / FOREACH performance. The FOR loop without length caching works 3 times slower on lists, comparing to arrays.
forEach loop: The forEach() method is also used to loop through arrays, but it uses a function differently than the classic “for loop”. It passes a callback function for each element of an array together with the below parameters: Current Value (required): The value of the current array element.
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);
}
foreach
is a C# language element. It plays by the rules of C#.
List<T>.ForEach
is a .NET Framework method. It plays by the rules of .NET, where foreach
doesn't exist.
This is an example of "language vs framework" confusion. Framework methods must work in many languages, and the languages (usually) have contradictory semantics.
Another example of this "language vs framework" confusion is the breaking change to Enumerable.Cast
between .net 3 and .NET 3.5. In .NET 3, Cast
used C# semantics. In .net 3.5, it was changed to use .net semantics.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With