这个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();
然后一切都按预期工作,测试通过 - 我想知道为什么会发生......
答案 0 :(得分:6)
我想知道为什么会发生这种情况
因为Select
返回一个迭代器,而不是一个列表或数组,它被懒惰地评估。您在foreach
期间迭代该迭代器一次,然后在Enumerable.All
期间再次另一次。这导致查询执行两次,每次都产生IEnumerable<T>
。
使用ToList
实现查询时,会导致创建一次列表,然后重复两次相同列表,从而在{{1}中产生所需的效果}。
答案 1 :(得分:4)
您永远不会创建列表(或任何类型的物化集合),只是枚举。 foreach
从Enumerable.Range()
一直创建了一个新的枚举,您的最终检查也使用Enumerable.Range()
的新枚举。
您必须创建一个集合或持久性的东西,以确保修改相同的元素。
枚举可以是物化集合,但并非必须如此。
在我看到它们时对术语进行澄清;
enumeration
是an iteration over a number of objects
给出;
枚举是迭代,但并非所有迭代都是枚举