为什么会发生这种情况的IEnumerable与列表
我有以下的BL方法为什么会发生这种情况的IEnumerable与列表
public static void SomeMethod (List<SomeClass> group)
{
IEnumerable<SomeClass> groupWithFalse =(from SomeClass gr in group
where gr.SomeProp== false
select gr);
foreach (SomeClass grFalse in groupWithFalse)
{
grFalse.Save();
}
if (groupWithFalse.Any())
{
// do some stuff
}
}
保存dl时的模拟实现(因为它在很多单元测试使用,不能被更改)是:
public void Save()
{
group.SomeProp = true;
}
如果我尝试单元测试流的最后一个语句,例如,如果(groupWithFalse.Any())
语句失败,显然没有更多的元素将该属性设置为false。 如果我在业务逻辑改变代码:
public static void SomeMethod (List<SomeClass> group)
{
List<SomeClass> groupWithFalse = new List<SomeClass>();
foreach (var g in group)
{
if (g.SomeProp == false)
groupWithFalse.Add(g);
}
foreach (SomeClass grFalse in groupWithFalse)
{
grFalse.Save();
}
if (groupWithFalse.Any())
{
// do some stuff
}
}
条件语句if (groupWithFalse.Any())
没有单元测试失败。为什么会发生?谢谢
运行一个LINQ查询不存储结果。
相反,每次枚举它时都会重新执行查询。
在致电Save()
后,查询将为空,因为没有任何项目符合where
子句。
将其更改为
var unsaved = group.Where(g => !g.SomeProp).ToList();
调用ToList()
将存储在一个List<T>
结果;这避免了重新执行查询。
你的问题在于LINQ的Lazy Evalution。在第一个例子中,LINQ查询实际上是TWICE运行的。首先枚举调用Save()方法,然后获取Any()。
作为使用LINQ的一般规则,在每个查询之后放置ToList()。
由于deferred execution,您的问题正在发生。在您的第一个示例中,groupWithFalse
不代表其中SomeProp
为false的对象列表,它指可以评估为所述列表的查询。
如果你想你的整个功能期间留在原地你的列表,只要在您的LINQ查询的末尾添加.ToList()
像这样:
IEnumerable<SomeClass> groupWithFalse =(from SomeClass gr in group
where gr.SomeProp == false
select gr).ToList();
// ^^^^^^^^^
这将导致查询立即执行并返回结果到一个List<SomeClass>
其内容将不会更改如果您修改其中的对象。
好了,在第一次执行正在发生的事情是,你正在定义一个迭代groupWithFalse
,超过有SomeProp
设置为false
的的group
元素进行迭代。你遍历groupWithFalse
并设置SomeProp
到true
所有这些元素,现在当你再次迭代(调用Any
产生第二迭代),每一个元素都有SomeProp
设置为true
因为你只是将其设置为某某当然Any
返回false
(迭代现在产生空集合)。
要详细一点,基本上当你说
IEnumerable<SomeClass> groupWithFalse =(from SomeClass gr in group
where gr.SomeProp== false
select gr);
要定义对象(
groupWithFalse
)捕获逻辑
“给我group
有SomeProp
设置为false元素序列。 “明确地说,这个对象实际上并不是SomeProp
设置为false的那些元素的集合或序列。当您第一次遍历此对象(foreach
)时,此规则将生成SomeProp
为false
(如果存在group
中的任何元素)的元素。但是当您第二次迭代时(Any
),此规则将不会生成任何元素,因为您已修改此规则从中抽取元素的集合group
。
在第二次迭代要检查是否List<SomeClass>
命名groupWithFalse
是空集,它是不是因为你已经添加元素,它在foreach
循环之前紧挨着它。
这是我会怎么写代码:
public static void SomeMethod (List<SomeClass> group) {
IEnumerable<SomeClass> groupWithFalse =(from SomeClass gr in group
where gr.SomeProp== false
select gr);
if(groupWithFalse.Any()) {
foreach (SomeClass grFalse in groupWithFalse) {
grFalse.Save();
}
// do some stuff
}
}
现在代码读起来像“获得的group
针对SomeProp
是false
的元素如果有Any
,Save
他们,然后做一些东西。”
在第一个示例中,您使用的是IEnumerable。把它想象成一个查询,而不是一个对象列表。那么你使用上面的查询遍历整个集合“group”,说“只迭代SomeProp等于false的元素”。
然后在您的foreach语句中,修改“组”列表中对象的状态。
然后你正在应用额外的谓词在你的查询中说“有任何元素在”组“列表中有一些prop等于false?你显然会得到一个答案:“不,没有他们”。
在第二个示例中,您只是获取元素的新集合,而不是查询。那么你可以遍历它,chage状态,然后问“是否有任何元素?”并回答“是”的答案。与之前有相同的元素,但改变了SomeProp属性值。
Google'linq lazy evaluation' – 2010-12-20 20:19:04