【问题标题】:Different LINQ Answer in VS 2010 and VS 2012VS 2010 和 VS 2012 中的不同 LINQ 答案
【发布时间】:2012-11-10 23:44:54
【问题描述】:

下面给出的答案是 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);
        }
    }
}

谢谢。

【问题讨论】:

  • Resharper 警告:访问修改后的闭包

标签: linq ienumerable lazy-evaluation


【解决方案1】:

您看到的是foreach 的两个不同应用程序的结果。 VS 2012 中的行为发生了变化。请参阅 this article

两者的区别在于foreach循环中d变量的作用域和生命周期。在 VS 2012 之前,只有一个 d 变量,所以这意味着您正在创建两个引用 相同 d 的闭包副本 (s =&gt; (s % d == 0)))。循环完成评估后,d 为 10。当您通过调用 .Distinct().Count() 执行查询时,两个闭包都将看到 d 的值 10。这就是为什么在 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)));
}

这大致是 VS 2012 生成的:

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

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

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

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

1 从技术上讲,只有在闭包中引用了迭代变量时。如果不是,则无需复制,因为这只会影响闭包语义。

【讨论】:

  • 感谢兄弟的详细信息。疑虑一扫而空。
  • 它与编译器版本的关系比与VS的关系更大,变化发生在C# 5.0:kristofmattei.be/2013/04/26/…
猜你喜欢
  • 2013-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-06
  • 2014-05-03
  • 2012-05-06
  • 2023-03-10
  • 2012-07-01
相关资源
最近更新 更多