有时Resharper会警告:
可能多次枚举IEnumerable
有an SO question on how to handle this issue,ReSharper网站也解释了here。它有一些示例代码,告诉您这样做:
IEnumerable<string> names = GetNames().ToList();
我的问题是关于这个具体的建议:这不会导致在2 for-each循环中两次枚举集合吗?
答案 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
PS,我在Resharper's documentation
上留下了以下评论嗨,
<块引用>您能否在文档中说清楚这只会是 如果 GetNames() 实现延迟执行会出现问题吗?
例如,如果 GetNames() 在幕后使用了 yield 或实现了一个 例如,像大多数 LINQ 语句一样的延迟执行方法 (.Select()、.Where() 等)
否则,如果在幕后 GetNames() 没有返回一个 IEnumerable 实现了延迟执行,那么就没有了 性能或数据完整性问题。例如。如果 GetNames 返回 列表