Different LINQ Answer in VS 2010 and VS 2012

穿精又带淫゛_ 提交于 2019-12-10 13:18:13

问题


The following gives answer as 1 in VS 2010 and 2 in VS 2012. I personally think it should be 2. I am not sure what's going on here.

using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System;

namespace _335ExamPreparation
{
    public class Doubts
    {
        int[] nums = { 10, 11, 12, 13, 14, 15, 16 };
        int[] divisors = { 7, 10 };

        static void Main(string[] args)
        {
            Doubts d = new Doubts();
            d.func();
        }

        public void func()
        {
            var m = Enumerable.Empty<int>();
            foreach (int d in divisors)
            {
                m = m.Concat(nums.Where(s => (s % d == 0)));
            }

            int count = m.Distinct().Count();
            Console.WriteLine(count);
        }
    }
}

Thanks.


回答1:


What you're seeing is the result of two different applications of foreach. The behavior was changed in VS 2012. See this article.

The difference between the two involves the scope and lifetime of the d variable in the foreach loop. Before VS 2012, there was only one d variable, so what this means is that you are creating two copies of a closure (s => (s % d == 0))) that both reference the same d. After the loop finishes being evaluated, d is 10. When you execute the query by calling .Distinct().Count(), both closures will see a value of 10 for d. This is why the count is 1 on VS 2010.

VS 2012 generates a different variable for each iteration1, so each closure will see a different instance of the d variable, the one that corresponds to that particular iteration.

This is roughly the code that VS 2010 generates:

int d;
for (int _index = 0; _index < divisors.Length; ++_index) {
    d = divisors[_index];
    m = m.Concat(nums.Where(s => (s % d == 0)));
}

And this is roughly what VS 2012 generates:

for (int _index = 0; _index < divisors.Length; ++_index) {
    int d = divisors[_index];
    m = m.Concat(nums.Where(s => (s % d == 0)));
}

The difference between these two should be readily apparent.

If you want to get the same behavior no matter which VS version, then always copy your iteration variable:

foreach (int d in divisors)
{
    var copy = d;
    m = m.Concat(nums.Where(s => (s % copy == 0)));
}

1 Technically, only if the iteration variable is referenced in a closure. If it's not then there is no need to make a copy as this only affects closure semantics anyway.



来源:https://stackoverflow.com/questions/13327224/different-linq-answer-in-vs-2010-and-vs-2012

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!