VS 2010和VS 2012中的不同LINQ答案

Var*_*rma 6 linq ienumerable lazy-evaluation

下面给出了VS 2010中的1和VS 2012中的2的答案.我个人认为它应该是2.我不确定这里发生了什么.

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

谢谢.

cdh*_*wie 16

您所看到的是两种不同应用的结果foreach.VS 2012中的行为已更改.请参阅此文章.

两者之间的差异涉及循环中d变量的范围和生命周期foreach.在VS 2012之前,只有一个d变量,所以这意味着你要创建一个s => (s % d == 0))两个引用相同 的闭包()的副本d.循环完成评估后,d为10.当您通过调用执行查询时.Distinct().Count(),两个闭包都将看到值10 d.这就是VS 2010上的计数为1的原因.

VS 2012为每个迭代1生成一个不同的变量,因此每个闭包将看到变量的不同实例,d即对应于该特定迭代的变量.

这大致是VS 2010生成的代码:

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

这与VS 2012产生的大致相同:

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

这两者之间的区别应该很明显.

如果你想获得相同的行为,无论哪个VS版本,那么总是复制你的迭代变量:

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

1从技术上讲,只有在闭包中引用迭代变量时.如果不是那么就没有必要复制,因为这只会影响闭包语义.