Search code examples
c#linqforeachcompiler-errors

Why does Linq ForEach not provide the same compiler error hints as a normal foreach loop?


List<(int integer, string str)> myTupleList;
        
foreach (var tuple in myTupleList)
    tuple = (tuple.integer, tuple.str);
    
myTupleList.ForEach(tuple =>
    tuple = (tuple.integer, tuple.str));

Why does the foreach give a compiler error, but not the Linq.ForEach?

Irrelevant details to get to 220 characters:

I wasted 2 hours because I forgot that you can't assign to the iteration variable in a foreach. While it was obviously my fault, it would have been nice to get a compiler error to save me some frustration. Why can't Linq.ForEach do this?


Solution

  • The variable you declare in foreach is considered a part of the foreach statement. The language spec calls it the "iteration variable". The syntax also looks distinct from a normal variable declaration, as it is followed by in. Because of this, it is more justified to add special restrictions to it, like not being able to reassign it.

    ForEach(x => ...) on the other hand, is not one single syntactic construct like foreach is. It is merely a call to a method called ForEach, and passes a lambda expression to that method. Unlike the iteration variable of a foreach, the x here is nothing special, just the parameter of a lambda expression.

    Lambda expressions represent "inline" method definitions - its parameters are like method parameters, which have always been reassignable. A language change such as "all lambda parameters are not allowed to be reassigned" would be hard to justify, and a change like "only the parameters of lambdas passed to methods named ForEach are not allowed to be reassigned" would not be practical to implement. In general it is difficult to know where a lambda is going to be passed.

    That said, the newly introduced in parameter modifier prevents method parameters from being reassigned, but alas, it is too late - changing ForEach to take a lambda with an in parameter would be a breaking change.