IEnumerable <t>和.Linq方法在哪里运行?

时间:2018-10-23 14:25:43

标签: c# linq ienumerable

我以为我对IEnumerable<T>了如指掌,但遇到了一个我无法解释的情况。当我们在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<CTest> GenerateEnumerableTest()
    {
        var tab = new List<int> { 2, 5, 10, 12 };

        return tab.Select(t => new CTest(t));
    }
}

到目前为止,还不错! 尽管我对IEnumerable<T>.Where linq方法有一定的了解,但以下测试却给了我一个意外的结果:

[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

任何人都可以解释为什么枚举制表符后获得原始值。 当然,在调用.ToList()方法之后,调用GenerateEnumerableTest()后一切正常。

2 个答案:

答案 0 :(得分:9)

var tab = CTest.GenerateEnumerableTest();

tab是LINQ查询,它生成CTest个实例,这些实例是根据int值初始化的,这些值来自一个永不改变的整数数组。因此,无论何时要求此查询,您都将获得“相同”实例(具有原始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?
}

答案 1 :(得分:1)

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

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