IEnumerable <T>和.Where Linq方法的行为?

Fab*_*rie 9 c# linq ienumerable

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

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

public class CTest
{
    public CTest(int amount)
    {
        Amount = amount;
    }

    public int Amount { get; set; }

    public override string ToString()
    {
        return $"Amount:{Amount}";
    }

    public static IEnumerable<CTest> GenerateEnumerableTest()
    {
        var tab = new List<int> { 2, 5, 10, 12 };

        return tab.Select(t => new CTest(t));
    }
}
Run Code Online (Sandbox Code Playgroud)

到目前为止没什么不好的!但是下面的测试给了我一个意想不到的结果,虽然我的知识IEnumerable<T>.Wherelinq方法:

[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);
    }
}
Run Code Online (Sandbox Code Playgroud)

选项卡中的任何项目都不会乘以2.输出将为:金额:2金额:5金额:10金额:12

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

Tim*_*ter 9

var tab = CTest.GenerateEnumerableTest();
Run Code Online (Sandbox Code Playgroud)

tab是一个LINQ查询,它生成CTestint-values 初始化的实例,这些实例来自一个永远不会改变的整数数组.因此,每当您要求此查询时,您将获得"相同"实例(使用原始实例Amount).

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

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

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?
}
Run Code Online (Sandbox Code Playgroud)


Sal*_*ari 1

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

var tab = CTest.GenerateEnumerableTest().ToList();
Run Code Online (Sandbox Code Playgroud)