Resharper的示例代码,用于解释“可能多次枚举IEnumerable”

时间:2013-11-21 18:28:51

标签: c# resharper resharper-7.1

有时Resharper会警告:

  

可能多次枚举IEnumerable

an SO question on how to handle this issue,ReSharper网站也解释了here。它有一些示例代码,告诉您这样做:

IEnumerable<string> names = GetNames().ToList();

我的问题是关于这个具体的建议:这不会导致在2 for-each循环中两次枚举集合吗?

5 个答案:

答案 0 :(得分:155)

GetNames()会返回IEnumerable。因此,如果您存储该结果:

IEnumerable foo = GetNames();

然后,每次枚举foo时,都会再次调用GetNames()方法(不是字面上,我找不到正确解释详细信息的链接,但请参阅IEnumerable.GetEnumerator())。

Resharper看到了这一点,并建议您将枚举GetNames()结果存储在局部变量中,例如通过在列表中实现它:

IEnumerable fooEnumerated = GetNames().ToList();

只要您引用GetNames(),这将确保fooEnumerated结果仅枚举一次。

这很重要,因为您通常只想枚举一次,例如当GetNames()执行(慢)数据库调用时。

因为您在列表中实现了结果,所以您再次列举fooEnumerated两次并不重要;你将两次迭代内存列表。

答案 1 :(得分:9)

我发现这是理解多个枚举的最佳和最简单的方法。

C#LINQ:可能多次枚举IEnumerable

https://helloacm.com/c-linq-possible-multiple-enumeration-of-ienumerable-resharper/

答案 2 :(得分:8)

GetNames()未被调用两次。每次要使用IEnumerable.GetEnumerator()枚举集合时,都会调用foreach的实现。如果在IEnumerable.GetEnumerator()范围内进行了一些昂贵的计算,则可能需要考虑这一点。

答案 3 :(得分:4)

是的,毫无疑问,你会两次枚举它。但关键是如果GetNames()返回一个懒惰的linq查询,计算成本非常高,那么它会计算两次,而不会调用ToList()ToArray()。< / p>

答案 4 :(得分:0)

仅仅因为方法返回 IEnumerable 并不意味着会延迟执行。

例如

IEnumerable<string> GetNames()
{
    Console.WriteLine("Yolo");
    return new string[] { "Fred", "Wilma", "Betty", "Barney" };
}

var names = GetNames(); // Yolo prints out here! and only here!

foreach(name in names)
{
    // Some code...
}

foreach(name in names)
{
    // Some code...
}

回到问题,如果:

一个。存在延迟执行(例如 LINQ - .Where()、.Select() 等):然后该方法返回一个知道如何迭代集合的“承诺”。所以当调用 .ToList() 时,这个迭代发生,我们将列表存储在内存中。

B.没有延迟执行(例如方法返回一个列表):然后假设 GetNames 返回一个列表,它基本上就像在该列表上执行 .ToList()

var names = GetNames().ToList(); 
//          1        2 3
  1. Yolo 打印出来
  2. 返回列表
  3. ReturnedList.ToList() 被调用

PS,我在Resharper's documentation

上留下了以下评论

嗨,

<块引用>

您能否在文档中说清楚这只会是 如果 GetNames() 实现延迟执行会出现问题吗?

例如,如果 GetNames() 在幕后使用了 yield 或实现了一个 例如,像大多数 LINQ 语句一样的延迟执行方法 (.Select()、.Where() 等)

否则,如果在幕后 GetNames() 没有返回一个 IEnumerable 实现了延迟执行,那么就没有了 性能或数据完整性问题。例如。如果 GetNames 返回 列表