为什么这会发生IEnumerable vs List

时间:2010-12-20 20:14:31

标签: c# .net

我有以下BL方法

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())在单元测试中不会失败。为什么会这样?感谢

5 个答案:

答案 0 :(得分:3)

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

调用Save()后,查询将为空,因为所有项都不符合where子句。

将其更改为

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

致电ToList()会将结果存储在List<T>;这可以避免重新执行查询。

答案 1 :(得分: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>,如果修改其中的对象,其内容将不会更改。

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

好的,在第一个实现中发生的事情是,您正在定义一个迭代器groupWithFalse,它迭代group设置为SomeProp的{​​{1}}元素。您已经迭代false并将groupWithFalse设置为SomeProp以获取所有这些元素,现在当您再次迭代时(对true的调用会产生第二次迭代), element将Any设置为SomeProp,因为您只是将其设置为当然,{}}}当然返回true(迭代现在生成空集合)。

详细说明,基本上当你说

Any

您正在定义一个对象(false),该对象捕获逻辑“给我IEnumerable<SomeClass> groupWithFalse =(from SomeClass gr in group where gr.SomeProp== false select gr); groupWithFalse设置为false的元素序列”。显然,此对象实际上不是group设置为false的那些元素的集合或序列。第一次迭代此对象(SomeProp)时,此规则将生成SomePropforeach的元素(如果SomeProp中有任何内容)。但是当您再次迭代(false)时,此规则将不会生成任何元素,因为您已修改此规则从中抽取其元素的集合group

在第二次迭代中,您正在检查名为Any的{​​{1}}是否为空集合,而不是因为您已在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他们然后做一些东西。“

答案 4 :(得分:0)

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

然后在你的foreach语句中修改“group”列表中对象的状态。

然后你在你的查询中应用了额外的谓词,指出“组”列表中是否有任何元素,其中某个prop等于false?“而且你显然得到了答案“没有他们没有”。

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