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();