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


76

有时Resharper警告:

IEnumerable的可能的多个枚举

关于如何处理此问题,有一个SO问题,ReSharper网站也在此处进行了说明。它有一些示例代码告诉您执行以下操作:

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

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


这回答了你的问题了吗?处理可能会多次枚举IEnumerable的警告
RBT

Answers:


183

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

IEnumerable foo = GetNames();

然后,每次您枚举时fooGetNames()都会再次调用该方法(从字面上看,我找不到正确解释其细节的链接,但请参阅IEnumerable.GetEnumerator())。

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

IEnumerable fooEnumerated = GetNames().ToList();

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

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

因为你物化在清单的结果,无所谓了,你枚举fooEnumerated两次; 您将遍历一个内存列表两次。


啊!这就解释了。
user2250250

1
否。GetEnumerator()方法在foreach循环中仅被调用一次。真正的原因是存在脏数据的风险。例如,在GetNames()中,有一个SQL查询,但只有返回IEnurable的查询。调用.ToList()时,您将所有数据存储在内存中,几乎没有脏数据的风险。但是,如果两次循环操作之间有很多时间,如果每次都将SQL执行到数据库中,那么存在脏数据的巨大风险。
罗宾

6
这是Microsoft的网址,用于告诉我们foreach循环的内部实现。您可以看到GetEnumerator()仅被调用一次。我们需要知道的另一件事是,IEnumerable在与某些ORM一起工作时是延迟加载。msdn.microsoft.com/zh-cn/library/aa664754(v=vs.71).aspx如果仅获取Enumerator的处理程序而未将所有数据加载到内存中,则两个操作之间可能存在一些冲突。同一枚举器,有人修改了数据库。为避免这种情况,Resharper建议您通过.ToList()方法将数据加载到Merroy中。
罗宾

5
这个答案有些不准确。并不是IEnumerable每个枚举都会对每个枚举进行评估,只有那些使用“延迟执行”实现的枚举才会被评估(这就是@CodeCaster所缺少的链接)。当不延迟执行基础实现时,可以安全地忽略R#警告,例如该问题示例要求ToList()将其存储在另一个IEnumerable变量中。
弗雷德里克

2
@CodeCaster,没问题,我来自一个最近的链接那里的答案,我在其中添加了关于缺少imo的简短说明。如果愿意,可以随时重用它。然后,一旦他们的观点不再相关,我可能会在这里删除我的评论。
弗雷德里克


9

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


5

是的,毫无疑问,您将对其进行两次枚举。但是要点是,如果GetNames()返回一个懒惰的linq查询,该查询的计算非常昂贵,那么它将在不调用or的情况下进行两次计算。ToList()ToArray()

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.