Link weird style with dictionaries

I came across a strange Linq behavior: with two linq expressions that might seem identical, I have different results! If I loop once, I get the same result, but find nothing above.

Here is the code:

Dictionary<String, String> mainDico = new Dictionary<String, String>(); mainDico.Add("key1", "value1"); mainDico.Add("key2", "value2"); List<Dictionary<String, String>> ls = new List<Dictionary<String, String>>(); Dictionary<String, String> fistDico = new Dictionary<String, String>(); fistDico.Add("key1", "value1"); fistDico.Add("key2", "value2"); Dictionary<String, String> secondDico = new Dictionary<String, String>(); secondDico.Add("key1", "other"); secondDico.Add("key2", "other"); ls.Add(fistDico); ls.Add(secondDico); IEnumerable<Dictionary<String, String>> failQuery = from p in ls select p; IEnumerable<Dictionary<String, String>> successfulQuery = from p in ls select p; String[] items = new String[] { "key1","key2" }; // with one element it works foreach (var item in items) { String temp = mainDico[item]; failQuery = failQuery.Where(p => p[item] == temp); successfulQuery = successfulQuery.Where(p => p[item] == mainDico[item]); } Console.WriteLine(successfulQuery.SingleOrDefault() != null);//True Console.WriteLine(failQuery.SingleOrDefault() != null);//False 
+4
source share
1 answer

The problem is that you are closing the loop variable.

The problematic section of code is here:

 foreach (var item in items) { String temp = mainDico[item]; failQuery = failQuery.Where(p => p[item] == temp); successfulQuery = successfulQuery.Where(p => p[item] == mainDico[item]); } 

You create a lambda that closes over item in the second case (and also in the first case, you must fix it), and you do not evaluate the request until the end of the foreach loop; this means that item will always be the last element in the foreach loop, not the current one. This can be easily solved by creating a new local variable, which is what you do in the first case, so this works.

Here is a link that discusses this issue in more detail. (You can find a lot more by searching for the variable "close over loop".

Please note that this has been changed in C # 5.0 , as this is a common cause of confusion and errors. (This is probably why some people could not reproduce the problem.)

It is also worth noting that this has nothing to do with the dictionary. In your request, item actually always the last element in foreach , not the current one, so it fails. All that you did with item , which relied on the fact that it was the current value, would not do what you wanted.

+5
source

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


All Articles