From Eric Lippert's blog: "Don't close the loop variable"

Possible duplicates:
Why is it bad to use an iteration variable in a lambda expression
C # - identifier and closing foreach

From Eric Lippert June 28, 2010 entry:

static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences) { // base case: IEnumerable<IEnumerable<T>> result = new[] { Enumerable.Empty<T>() }; foreach(var sequence in sequences) { var s = sequence; // don't close over the loop variable // recursive case: use SelectMany to build the new product out of the old one result = from seq in result from item in s select seq.Concat(new[] {item}); } return result; } 

var s = sequence; Looks like no-op. Why is he not alone? What is wrong when sequence used directly?

And, more subjectively: to what extent is this considered a wart in C # behavior?

+2
source share
4 answers

A few related articles by Eric himself, as well as an interesting discussion in the comments:

+4
source

This is a subtle issue with an area that is related to how close and deferred execution work.

If you do not use a local variable, but immediately follow instead, the IEnumarable result is bound to the VARIABLE sequence, not the VALUE sequence, and during query execution, the VARIABLE sequence contains LAST VALUE of the sequence.

If you declare another local variable, as in Eric's example, the scope is limited to each iteration of the loop. Therefore, it will be evaluated as intended, even if implementation is delayed.

+3
source

The LINQ query used here causes the s value to be available outside the scope of the definition where it was originally defined (that is, the CartesianProduct method). This is what is called closure . Due to deferred execution, by the time the LINQ query is actually evaluated (provided that it is eventually evaluated), the allocation method will be completed and s will be “out of scope”, at least in accordance with the traditional rule area. Despite this, in this context it is still “safe” to refer to s .

Closing is very convenient and well-behaved in the traditional functional programming language, where things are naturally immutable. And the fact that C # is the main imperative programming language, where variables are changed by default, is the basis for the problem that leads to this strange work.

By creating an intermediate variable as part of the loop, you actually instruct the compiler to allocate a separate, non-shared variable for each iteration of the LINQ query. Otherwise, each iteration will have the same instance of the variable, which will also be (obviously) the same value ... probably not what you want.

+1
source

One of the comments on this blog post:

However, there is a mistake in your first version of the CartesianProduct method: you close the loop of a variable, so because of delayed execution, it makes the Cartesian product of the last sequence with itself. You need to add a temporary local variable inside the foreach loop to make it work (the second version works fine, though).

0
source

Source: https://habr.com/ru/post/1339029/


All Articles