有时 Resharper 会警告:
IEnumerable 可能的多重枚举
有一个关于如何处理这个问题的 SO question,ReSharper 网站也在这里解释了一些事情。它有一些示例代码告诉您这样做:
IEnumerable<string> names = GetNames().ToList();
我的问题是关于这个具体建议:这是否仍会导致在 2 个 for-each 循环中对集合进行两次枚举?
有时 Resharper 会警告:
IEnumerable 可能的多重枚举
有一个关于如何处理这个问题的 SO question,ReSharper 网站也在这里解释了一些事情。它有一些示例代码告诉您这样做:
IEnumerable<string> names = GetNames().ToList();
我的问题是关于这个具体建议:这是否仍会导致在 2 个 for-each 循环中对集合进行两次枚举?
GetNames()
返回一个IEnumerable
. 因此,如果您存储该结果:
IEnumerable foo = GetNames();
然后每次枚举foo
时,都会再次调用该GetNames()
方法(不是字面意思,我找不到正确解释细节的链接,但请参阅IEnumerable.GetEnumerator()
)。
Resharper 看到了这一点,并建议您将枚举结果GetNames()
存储在局部变量中,例如通过在列表中实现它:
IEnumerable fooEnumerated = GetNames().ToList();
这将确保GetNames()
结果只枚举一次,只要您引用fooEnumerated
.
这确实很重要,因为您通常只想枚举一次,例如在GetNames()
执行(慢速)数据库调用时。
因为您在列表中具体化fooEnumerated
了结果,所以您枚举两次并不重要。您将遍历内存列表两次。
我发现这是理解多个枚举的最佳和最简单的方法。
C# LINQ:IEnumerable 的可能多重枚举
https://helloacm.com/c-linq-possible-multiple-enumeration-of-ienumerable-resharper/
GetNames()
不会被调用两次。IEnumerable.GetEnumerator()
每次您想用 枚举集合时都会调用的实现foreach
。如果进行IEnumerable.GetEnumerator()
一些昂贵的计算,这可能是一个需要考虑的理由。
是的,毫无疑问,您将枚举它两次。但关键是如果GetNames()
返回一个计算起来非常昂贵的惰性 linq 查询,那么它将计算两次而不调用ToList()
or ToArray()
。
仅仅因为一个方法返回 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() 时,会发生这种迭代,我们将列表存储在内存中。
湾。没有延迟执行(例如方法返回一个列表):然后假设 GetNames 返回一个列表,它基本上就像在该列表上执行 .ToList()
var names = GetNames().ToList();
// 1 2 3
PS,我在Resharper 的文档上留下了以下评论
你好,
您能否在文档中明确说明,如果 GetNames() 实现延迟执行,这只会是一个问题?
例如,如果 GetNames() 在后台使用 yield 或像大多数 LINQ 语句一样实现延迟执行方法,例如(.Select()、.Where() 等)
否则,如果 GetNames() 没有返回实现延迟执行的 IEnumerable,则此处不存在性能或数据完整性问题。例如,如果 GetNames 返回 List