0

In this slice of code i'm adding ColumnInfo's to a list, In my view the getter expression passed to the ColumnInfo get's called upon my rows. This all works fine except for the local variable "childt.Naam" that get's used in my lambda expression.

The runtime evaluation causes childt.Naam to always be the one of the last childt passed in the foreach.

How can I make sure these "local variables" get passed correctly to the expression

 foreach (var childt in itemt.ItemTemplates)
            {                 

                    columns.Add(new ColumnInfo<Item>(model => level(model).GetV(childt.Naam, model.Taal) + childt.Naam, childt.Naam, new TextPropertyRenderer(), editable));



            }

Here's the relevant parts of constructor of the ColumnInfo class

    public ColumnInfo(Expression<Func<TModel, object>> getter, string title, IPropertyRenderer renderer, bool editable = false)
            {
                this.renderer = renderer;
                this.Editable = editable;
                this.Getter = getter.Compile();
}
Michiel Cornille
  • 2,067
  • 1
  • 19
  • 42

3 Answers3

2

It is because of the deferred aspect of LINQ so you need to create separate space for values.

foreach (var childt in itemt.ItemTemplates)
{                 
       var naam = childt.Naam;
       columns.Add(new ColumnInfo<Item>(model => 
             level(model).GetV(naam, model.Taal) + naam,
             naam,
             new TextPropertyRenderer(), 
             editable));

}

http://msdn.microsoft.com/en-us/library/bb882641.aspx

Adrian Iftode
  • 15,465
  • 4
  • 48
  • 73
2

You are closing over the loop variable - just make local copy:

foreach (var childt in itemt.ItemTemplates)
{                 
   var localChildt = childt;
   columns.Add(new ColumnInfo<Item>(model => level(model).GetV(localChildt.Naam, model.Taal) + localChildt.Naam, localChildt.Naam, new TextPropertyRenderer(), editable));
 }

Also see "Closing over the loop variable considered harmful"

BrokenGlass
  • 158,293
  • 28
  • 286
  • 335
1

This issue is known as 'access to modified closure'. To fix it, create a local variable in the loop body, assign childt to it, and use it in the lambda expressions:

foreach (var childt in itemt.ItemTemplates)
{                 
     var c = childt;
     columns.Add(new ColumnInfo<Item>(model => level(model).GetV(c.Naam, model.Taal) + c, c.Naam, new TextPropertyRenderer(), editable));
}
Community
  • 1
  • 1
Yuriy Guts
  • 2,180
  • 1
  • 14
  • 18