Changing an object affects previous versions of an object in a foreach loop

So, I think this is pretty straight forward.

Here is my code:

Dictionary<int, IEnumerable<SelectListItem>> fileTypeListDict = new Dictionary<int, IEnumerable<SelectListItem>>(); foreach (PresentationFile pf in speakerAssignment.FKPresentation.PresentationFiles) { IEnumerable<SelectListItem> fileTypes = Enum.GetValues(typeof(PresentationFileType)) .Cast<PresentationFileType>().Select(x => new SelectListItem { Text = x.ToString(), Value = Convert.ToString((int)x), Selected = pf.Type == (int)x }); fileTypeListDict.Add(pf.ID, fileTypes); } 

It happens that in the end the dictionary will have all the correct keys, but all the values ​​will be set to the fileTypes list created during the last iteration of the loop. I'm sure this has something to do with the object that is used as a reference, but have not seen this problem before in my time with C #. Does anyone want to explain why this is happening and how should I solve this problem?

Thanks!

+6
source share
1 answer

This is the notorious problem of “ foreach capture” and “fixed” in C # 5 (“fixed” is a strong word, as it suggests it was a “mistake”: in fact, the specification has now changed to recognize this as a common cause of confusion ) In both cases, lambda captures the variable pf , not the " pf value during this iteration", but in C # up to -5 the pf variable is technically outside the loop (therefore there is only one of them, period), where - like in C # 5 and above, the variable is limited inside the loop (so there is another variable for each goal).

In C # 4, just trick:

 foreach (PresentationFile tmp in speakerAssignment.FKPresentation.PresentationFiles) { PresentationFile pf = tmp; //... as before 

now pf is inside the foreach and it will work fine. Without this, there is only one pf - and since you have deferred execution to the end, the value of a single pf will be the last iteration.

An alternative solution would be: do not delay execution:

 fileTypeListDict.Add(pf.ID, fileTypes.ToList()); // note the ToList 

Now it is evaluated when pf is "current", so it will have the expected results.

+9
source

All Articles