Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Question about foreach and delegates

Suppose the following code:

foreach(Item i on ItemCollection)
{
   Something s = new Something();
   s.EventX += delegate { ProcessItem(i); };
   SomethingCollection.Add(s);
}

Of course, this is wrong because all the delegates points to the same Item. The alternative is:

foreach(Item i on ItemCollection)
{
   Item tmpItem = i;
   Something s = new Something();
   s.EventX += delegate { ProcessItem(tmpItem); };
   SomethingCollection.Add(s);
}

In this case all the delegates point to their own Item.

What about this approach? There is any other better solution?

like image 537
FerranB Avatar asked Oct 21 '09 12:10

FerranB


2 Answers

UPDATE: There is extensive analysis and commentary on this issue here:

http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/


This is an extremely frequently reported issue; usually it is reported as a compiler bug, but in fact the compiler is doing the right thing according to the specification. Anonymous functions close over variables, not values, and there is only a single foreach loop variable. Therefore each lambda closes over the same variable, and therefore gets the current value of that variable.

This is surprising to almost everyone, and leads to much confusion and many bug reports. We are considering changing the specification and implementation for a hypothetical future version of C# so that the loop variable is logically declared inside the looping construct, giving a "fresh" variable every time through the loop.

This would be a breaking change, but I suspect the number of people who depend on this strange behaviour is quite low. If you have opinions on this subject, feel free to add comments to the blog posts mentioned up the update above. Thanks!

like image 186
Eric Lippert Avatar answered Oct 06 '22 13:10

Eric Lippert


The second chunk of code is just about the best approach you can get all other things staying the same.

However, it may be possible to create a property on Something which takes Item. In turn the event code could access this Item off the sender of the event or it might be included in the eventargs for the event. Hence eliminating the need for the closure.

Personally I've added "Elimination of Unnecessary Closure" as a worthwhile refactoring since it can be difficult to reason on them.

like image 28
AnthonyWJones Avatar answered Oct 06 '22 13:10

AnthonyWJones