与严格评估的列表相比,为什么(懒惰)LINQ查询“行为奇怪”?

时间:2010-12-22 07:23:06

标签: c# linq

为什么这个测试失败了?

        private class TestClass
        {
            public string Property { get; set; }
        }

        [Test]
        public void Test()
        {
            var testClasses = new[] { "a", "b", "c", "d" }
                .Select(x => new TestClass());

            foreach(var testClass in testClasses)
            {
                testClass.Property = "test";
            }

            foreach(var testClass in testClasses)
            {
                Assert.That(!string.IsNullOrEmpty(testClass.Property));
            }
        }

问题显然与Select语句中的延迟屈服有关,因为如果我在Select()方法之后添加.ToList()调用,则测试通过。

3 个答案:

答案 0 :(得分:5)

由于LINQ的工作原理,您实际上正在创建 8 不同版本的TestClass - 每foreach一套4。基本上就像你有:

var testClasses = new[] { "a", "b", "c", "d" };
foreach(var testClass in testClasses.Select(x => new TestClass()))
{
    testClass.Property = "test";
}

foreach(var testClass in testClasses.Select(x => new TestClass()))
{
    Assert.That(!string.IsNullOrEmpty(testClass.Property));
}

第一组(丢弃)具有属性集。

通过在ToList()末尾调用testClasses,强制它存储并重复使用相同的4 TestClass个实例,因此它会通过。

答案 1 :(得分:2)

每次迭代testClasses变量时,都将运行.Select() lambda表达式中的代码。在您的情况下,效果是不同的foreach循环获得TestClass个对象的不同实例。

正如您所注意到的那样,在查询结尾处粘贴.ToList()将确保仅执行一次。

答案 2 :(得分:0)

这是因为选择返回IEnumerable<T>等LINQ扩展方法是懒惰的。试着更加渴望:

var testClasses = new[] { "a", "b", "c", "d" }
    .Select(x => new TestClass())
    .ToArray();