迭代linq创建的集合时,C#foreach不会修改元素的属性.Select()

时间:2015-06-23 10:12:40

标签: c# linq

这个Nunit测试没有通过(我期望它)。

class ReferenceObject
{
    public bool Flag { get; set; }
}

[Test]
public void CSharpWhyYouNoWork()
{
    //given
    var someRange = Enumerable.Range(0, 3);

    var selectedEnumerable = someRange.Select(p => new ReferenceObject());

    //when
    foreach (var foreachIterationVariable in selectedEnumerable)
    {
        foreachIterationVariable.Flag = true;
    }

    //then
    Assert.That(selectedEnumerable.All(p => p.Flag));
}

我认为foreach迭代变量是通过引用传递的,所以我希望它能修改集合的对象。

有趣的是,如果我使用了一个列表,那就是:

someRange.Select(...).ToList();

然后一切都按预期工作,测试通过 - 我想知道为什么会发生......

2 个答案:

答案 0 :(得分:6)

  

我想知道为什么会发生这种情况

因为Select返回一个迭代器,而不是一个列表或数组,它被懒惰地评估。您在foreach期间迭代该迭代器一次,然后在Enumerable.All期间再次另一次。这导致查询执行两次,每次都产生IEnumerable<T>

使用ToList实现查询时,会导致创建一次列表,然后重复两次相同列表,从而在{{1}中产生所需的效果}。

答案 1 :(得分:4)

您永远不会创建列表(或任何类型的物化集合),只是枚举。 foreachEnumerable.Range()一直创建了一个新的枚举,您的最终检查也使用Enumerable.Range()的新枚举。

您必须创建一个集合或持久性的东西,以确保修改相同的元素。

枚举可以是物化集合,但并非必须如此。

在我看到它们时对术语进行澄清;

  

enumerationan iteration over a number of objects

给出;

  

枚举是迭代,但并非所有迭代都是枚举