2013-11-21 49 views
58

有时ReSharper的警告一下:IEnumerable的ReSharper的示例代码

可能多个枚举

an SO question on how to handle this issue,并ReSharper的网站也解释了事情here。它有一些示例代码,告诉你这样做,而不是:

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

我的问题是关于这个具体建议:不会这还导致通过集合中的2-每个循环枚举两次?

回答

146

GetNames()返回IEnumerable。所以,如果你存储结果:

IEnumerable foo = GetNames(); 

然后每次枚举foo时,GetNames()方法再次被调用(不夸张地说,我无法找到一个链接,正确解释的细节,但看到IEnumerable.GetEnumerator())。

ReSharper的看到了这一点,并通过在列表中去实现它建议你存储在一个局部变量枚举GetNames(),例如的结果

IEnumerable fooEnumerated = GetNames().ToList(); 

这将确保该GetNames()结果只列举一次,只要你参考fooEnumerated

这件事情确实是因为你通常要一次列举,例如当GetNames()执行(慢)数据库调用。

因为你物化结果在列表中,你再次列举fooEnumerated两次都没关系;您将两次遍历内存列表。

+0

啊可能的多个枚举!这解释了它。 – user2250250

+0

否。只有在foreach循环中调用GetEnumerator()方法一次。真正的原因是脏数据的风险。例如,在GetNames()中,有一个SQL查询,但只有返回IEnurable的查询。当调用.ToList()时,将所有数据存储在内存中,脏数据的风险很小。但是如果在2次循环之间有很多时间,如果每次都对数据库执行SQL,那么脏数据的风险很大。 –

+0

@SunRobin这是一个以简化形式呈现真相的答案,正如也在其中提到的那样。我还没有到处去改进它。您使用“脏数据”可能需要进一步解释。 – CodeCaster

4

是的,你会毫无疑问地列举两次。但问题是,如果GetNames()返回一个懒惰的LINQ查询这是计算则非常昂贵,将计算两次没有到ToList()ToArray()通话。

7

GetNames()不会被调用两次。每次您想要使用foreach来枚举集合时,都会调用IEnumerable.GetEnumerator()的实现。如果在IEnumerable.GetEnumerator()内进行了一些昂贵的计算,这可能是一个需要考虑的原因。