为什么LINQ会以两种不同方式处理"相同"的方法?

Mic*_*ael 2 c# linq

我今天遇到了一个有趣的问题,我有两种方法,一目了然,它们都做同样的事情.那是返回一个IEnumerable的Foo对象.

我在下面将它们定义为List1和List2:

public class Foo
{
    public int ID { get; set; }
    public bool Enabled { get; set;}    
}

public static class Data
{
    public static IEnumerable<Foo> List1
    {
        get
        {
            return new List<Foo>
            {
                new Foo {ID = 1, Enabled = true},
                new Foo {ID = 2, Enabled = true},
                new Foo {ID = 3, Enabled = true}
            };
        }
    }

    public static IEnumerable<Foo> List2
    {
        get
        {
            yield return new Foo {ID = 1, Enabled = true};
            yield return new Foo {ID = 2, Enabled = true};
            yield return new Foo {ID = 3, Enabled = true};
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在考虑以下测试:

IEnumerable<Foo> listOne = Data.List1;
listOne.Where(item => item.ID.Equals(2)).First().Enabled = false;
Assert.AreEqual(false, listOne.ElementAt(1).Enabled);
Assert.AreEqual(false, listOne.ToList()[1].Enabled);  

IEnumerable<Foo> listTwo = Data.List2;
listTwo.Where(item => item.ID.Equals(2)).First().Enabled = false;
Assert.AreEqual(false, listTwo.ElementAt(1).Enabled);
Assert.AreEqual(false, listTwo.ToList()[1].Enabled);  
Run Code Online (Sandbox Code Playgroud)

这两种方法似乎做了"相同"的事情.

为什么测试代码中的第二个断言失败了?
为什么listTwo的第二个"Foo"项目在listOne中时没有设置为false?

注意:我之前解释了为什么允许这种情况发生以及这两者的区别是什么.不是如何修复第二个断言,因为我知道如果我向List2添加ToList调用它将起作用.

Jon*_*upp 6

第一个代码块构建一次项目并返回包含项目的列表.

每次IEnumerable走过时,第二个代码块都会构建这些项目.

这意味着第一个块的第二行和第三行在同一个对象实例上运行.第二个块的第二行和第三行在Foo的不同实例上运行(在迭代时创建新实例).

查看此方法的最佳方法是在方法中设置断点并在调试器下运行此代码.第一个版本只会打破一次断点.第二个版本将打两次,一次是在.Where()调用期间,一次是在.ElementAt调用期间.(编辑:使用修改后的代码,它也会在ToList()调用期间第三次命中断点.)

这里要记住的是,迭代器方法(即它使用yield return)将在每次迭代枚举器时运行,而不仅仅是在构造初始返回值时.