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

use*_*250 69 c# resharper resharper-7.1

有时Resharper会警告:

可能的多个枚举IEnumerable

关于如何处理这个问题还有一个问题,ReSharper网站也在这里解释了一些事情.它有一些示例代码,告诉您这样做:

IEnumerable<string> names = GetNames().ToList();
Run Code Online (Sandbox Code Playgroud)

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

Cod*_*ter 167

GetNames()返回一个IEnumerable.因此,如果您存储该结果:

IEnumerable foo = GetNames();
Run Code Online (Sandbox Code Playgroud)

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

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

IEnumerable fooEnumerated = GetNames().ToList();
Run Code Online (Sandbox Code Playgroud)

GetNames()只要您参考,这将确保仅枚举一次结果fooEnumerated.

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

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

  • 这是微软的url告诉我们foreach循环的内部实现.您可以看到GetEnumerator()仅被调用一次.另一件我们需要知道的是IEnumerable在使用某些ORM时是延迟加载的.https://msdn.microsoft.com/en-us/library/aa664754(v=vs.71).aspx如果您刚刚获得Enumerator的处理程序而未将所有数据加载到内存中,则可能会在使用相同枚举器的2个操作,有人修改了数据库.为避免这种情况,Resharper建议您通过.ToList()方法将数据加载到Merroy中. (6认同)
  • 这个答案有点不精确.并非每个枚举的IEnumerable都被评估,只有那些用['延迟执行']实现的那些(http://blogs.msdn.com/b/charlie/archive/2007/12/09/deferred-execution. aspx)(这是你缺少的链接,@ CodeCaster).当底层实现没有延迟执行时,可以安全地忽略R#警告,就像这个调用`ToList()`将它存储在另一个`IEnumerable`变量中的问题示例一样. (5认同)
  • 不会。GetEnumerator() 方法只会在 foreach 循环中调用一次。真正的原因是脏数据的风险。例如,在GetNames()中,有一个SQL查询,但只有返回IEnurable的查询。当您调用 .ToList() 时,您将所有数据存储在内存中,脏数据的风险很小。但如果两次循环操作之间的时间间隔较长,每次都对数据库执行SQL,那么就有很大的脏数据风险。 (2认同)
  • @CodeCaster,没问题,我是从最近的答案中找到的,我在其中添加了一个简短的[解释](/ questions/35635954/counting-non-faulted-tasks-cause-re-execution-of-each-任务#comment58955158_35636420)缺乏imo.如果您愿意,请随意重复使用.一旦他们的诡计不再相关,我可能会删除我的评论. (2认同)

Rya*_*yan 11

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

C#LINQ:IEnumerable可能的多次枚举

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


小智 8

GetNames()没有被叫两次.IEnumerable.GetEnumerator()每次要枚举集合时都会调用实现foreach.如果在IEnumerable.GetEnumerator()一些昂贵的计算中,这可能是一个考虑的理由.


Sri*_*vel 5

是的,毫无疑问,您会枚举它两次。但关键是如果GetNames()返回一个计算非常昂贵的惰性 linq 查询,那么它将计算两次而不调用ToList()or ToArray()