Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does List<T>.ForEach() implement a for loop?

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?

like image 717
Dave New Avatar asked Jul 15 '12 15:07

Dave New


People also ask

Why do we use foreach loop?

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.

What can't foreach loops be used for?

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.

Why is foreach better than for loop?

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.

Can we use foreach instead of for loop?

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.


2 Answers

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);
}
like image 104
Michael Liu Avatar answered Sep 24 '22 20:09

Michael Liu


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.

like image 45
Amy B Avatar answered Sep 25 '22 20:09

Amy B