IEnumerable 和。Where Linq方法的行为?

我以为我知道关于IEnumerable一切,但我刚遇到一个我无法解释的案例。 当我们调用。在IEnumerable上使用linq方法时,执行被推迟到对象被枚举,不是吗?

那么如何解释下面的示例:

 public class CTest { public CTest(int amount) { Amount = amount; } public int Amount { get; set; } public override string ToString() { return $"Amount:{Amount}"; } public static IEnumerable GenerateEnumerableTest() { var tab = new List { 2, 5, 10, 12 }; return tab.Select(t => new CTest(t)); } } 

到目前为止没什么不好的! 但是下面的测试给了我一个意想不到的结果,虽然我对IEnumerable.Where linq方法.Where

 [TestMethod] public void TestCSharp() { var tab = CTest.GenerateEnumerableTest(); foreach (var item in tab.Where(i => i.Amount > 6)) { item.Amount = item.Amount * 2; } foreach (var t in tab) { var s = t.ToString(); Debug.Print(s); } } 

标签中的任何项目都不会乘以2.输出结果为:金额:2金额:5金额:10金额:12

有没有人可以解释为什么枚举选项卡后,我得到原始值。 当然,在调用GenerateEnumerableTest()方法之后调用.ToList()之后一切正常。

 var tab = CTest.GenerateEnumerableTest(); 

tab是一个LINQ查询,它生成从int初始化的CTest实例,这些int来自一个永不改变的整数数组。 因此,每当您要求此查询时,您将获得“相同”实例(具有原始Amount )。

如果要“实现”此查询,可以使用ToList然后更改它们。 否则,您正在修改仅存在于第一个foreach循环中的CTest实例。 第二个循环使用未修改的Amount枚举其他CTest实例。

因此查询包含如何获取项目的信息,您也可以直接调用该方法:

 foreach (var item in CTest.GenerateEnumerableTest().Where(i => i.Amount > 6)) { item.Amount = item.Amount * 2; } foreach (var t in CTest.GenerateEnumerableTest()) { // now you don't expect them to be changed, do you? } 

与许多LINQ操作一样, Select是惰性的并且使用延迟执行,因此您的lambda表达式永远不会被执行,因为您正在调用Select但从不使用结果。 这就是为什么在调用GenerateEnumerableTest()方法之后调用.ToList()后一切正常的原因:

 var tab = CTest.GenerateEnumerableTest().ToList();