2010-12-20 82 views
0

我有以下的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())没有单元测试失败。为什么会发生?谢谢

+0

Google'linq lazy evaluation' – 2010-12-20 20:19:04

回答

3

运行一个LINQ查询不存储结果。
相反,每次枚举它时都会重新执行查询。

在致电Save()后,查询将为空,因为没有任何项目符合where子句。

将其更改为

var unsaved = group.Where(g => !g.SomeProp).ToList(); 

调用ToList()将存储在一个List<T>结果;这避免了重新执行查询。

0

你的问题在于LINQ的Lazy Evalution。在第一个例子中,LINQ查询实际上是TWICE运行的。首先枚举调用Save()方法,然后获取Any()。

作为使用LINQ的一般规则,在每个查询之后放置ToList()。

2

由于deferred execution,您的问题正在发生。在您的第一个示例中,groupWithFalse不代表其中SomeProp为false的对象列表,它指可以评估为所述列表的查询。

如果你想你的整个功能期间留在原地你的列表,只要在您的LINQ查询的末尾添加.ToList()像这样:

IEnumerable<SomeClass> groupWithFalse =(from SomeClass gr in group 
             where gr.SomeProp == false 
             select gr).ToList(); 
//            ^^^^^^^^^ 

这将导致查询立即执行并返回结果到一个List<SomeClass>其内容将不会更改如果您修改其中的对象。

0

好了,在第一次执行正在发生的事情是,你正在定义一个迭代groupWithFalse,超过有SomeProp设置为false的的group元素进行迭代。你遍历groupWithFalse并设置SomeProptrue所有这些元素,现在当你再次迭代(调用Any产生第二迭代),每一个元素都有SomeProp设置为true因为你只是将其设置为某某当然Any返回false(迭代现在产生空集合)。

要详细一点,基本上当你说

IEnumerable<SomeClass> groupWithFalse =(from SomeClass gr in group 
             where gr.SomeProp== false 
             select gr); 
要定义对象( groupWithFalse)捕获逻辑

“给我groupSomeProp设置为false元素序列。 “明确地说,这个对象实际上并不是SomeProp设置为false的那些元素的集合或序列。当您第一次遍历此对象(foreach)时,此规则将生成SomePropfalse(如果存在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针对SomePropfalse的元素如果有AnySave他们,然后做一些东西。”

0

在第一个示例中,您使用的是IEnumerable。把它想象成一个查询,而不是一个对象列表。那么你使用上面的查询遍历整个集合“group”,说“只迭代SomeProp等于false的元素”。

然后在您的foreach语句中,修改“组”列表中对象的状态。

然后你正在应用额外的谓词在你的查询中说“有任何元素在”组“列表中有一些prop等于false?你显然会得到一个答案:“不,没有他们”。

在第二个示例中,您只是获取元素的新集合,而不是查询。那么你可以遍历它,chage状态,然后问“是否有任何元素?”并回答“是”的答案。与之前有相同的元素,但改变了SomeProp属性值。

相关问题