4

Possible Duplicate:
Why is it bad to use a iteration variable in a lambda expression

Why do I get: "iteration variable in a lambda expression may have unexpected results"? Suppose I have the following code:

  Dim writeAbleColumns As String() = {"IsSelected", "IsFeeExpense", "IsSubscriptionRedemption"}
  With grid
     For Each column As DataGridViewColumn In .Columns
      column.ReadOnly = Not Array.Exists(writeAbleColumns, Function(arrElement) column.Name = arrElement)
      Next
  End With

I get the warning:

Warning 1   Using the iteration variable in a lambda expression may have unexpected results.  Instead, create a local variable within the loop and assign it the value of the iteration variable.

I don't understand why changing my code to the following changes anything:

  Dim writeAbleColumns As String() = {"IsSelected", "IsFeeExpense", "IsSubscriptionRedemption"}
  With grid
     For Each column As DataGridViewColumn In .Columns
      Dim c As DataGridViewColumn = column
      column.ReadOnly = Not Array.Exists(writeAbleColumns, Function(arrElement) c.Name = arrElement)
      Next
  End With

Fundamentally nothing changes except the warning disappears. I just have another variable point to my variable. Why the warning? What unexpected things might happen?

Community
  • 1
  • 1
Denis
  • 11,796
  • 16
  • 88
  • 150

2 Answers2

15

The lambda is bound to the variable, not to the value the variable had when the lambda was turned into a delegate. As the loop variable updates, every lambda created bound to that variable also sees the changed value of the variable, which you might not want. Creating a new variable every time you iterate the loop binds each lambda over a new, different, unchanging variable.

This is a major pain point in C# and VB. In C# 5 and VB 11 we are changing the loop closure semantics to mitigate this problem.

For more information see

Is there a reason for C#'s reuse of the variable in a foreach?

and the last few paragraphs of Tim's article:

http://msdn.microsoft.com/en-us/magazine/cc163362.aspx

and my article:

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

Community
  • 1
  • 1
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Can we get a patch to the C#3 and C#4 compilers too? I run into this every day and our company is just now jumping on the C#4 bandwagon. I can't wait for C#5, literally. – James Dunne Mar 02 '12 at 00:45
  • @JamesDunne, are you sure you actually mean versions of C# and not .Net? Because C# 5 compiler can target for example .Net 4.0, or even older ones. – svick Mar 02 '12 at 01:30
  • 1
    @svick: Yes, of course the compiler can target older platforms, but that is irrelevant when you want Visual Studio, a suite of TFS build servers, and all your team's compilers to all be the latest version still targeting an older platform. Practically speaking, it would be easier and cheaper to get a (hopefully free) patch on the older compiler version than to buy the newest version of the entire suite just to get this bug fixed. – James Dunne Mar 02 '12 at 16:51
  • 3
    @JamesDunne: We are not planning on silently changing the behaviour of the existing compilers, no. Sorry! – Eric Lippert Mar 02 '12 at 18:28
4

In your case, nothing can go wrong.

The issue is if that lambda gets saved and used after the loop continues (for example, used in an event handler). Eric Lippert has a very nice explanation on his blog.

Oh, and this question already has a very long list of references here on SO.

Community
  • 1
  • 1
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720