使用大量迭代器在C#中对内存使用的影响是什么?假设一个程序执行了数千个foreach循环 - 每个循环是否通过调用GetEnumerator在堆上分配临时对象?CLR是否执行任何类型的优化(例如,IEnumerator对象的堆栈分配)?还是这根本不是一个值得担心的重要问题?
对于大多数情况来说,它并不足够重要,不必担心。正如Eric所指出的那样,在我的经验中,这可能是一些特殊情况下才显得重要,但这种情况相当罕见。
如果你正在执行成千上万个foreach
循环,那么这些循环内部实际上在进行工作,这几乎肯定比迭代器本身更加重要。
请注意,使用foreach
遍历数组(已在编译时确定为数组)不使用IEnumerable<T>
,而是使用直接索引。但我不会因此改变我的代码。
同以往一样,如果您关注性能,您需要对其进行测量和分析。瓶颈几乎永远不会出现在您预期的位置。
通常编译器可以将foreach循环优化为简单循环,只需要一个索引变量在堆栈中(或处理器寄存器中)。
如果仍使用迭代器,则大多数迭代器都是结构体,因此它们只分配在堆栈上而不是堆上。
那些作为类的少数迭代器仍然非常小且快速。您可以创建数百万个它们而没有明显的影响。
yield return
语句等)创建的IEnumerable
/IEnumerator
实现通常都是类,尽管这并不完全由规范保证。 - Jon Skeetforeach
表达式中使用的静态类型为List<T>
而不是(比如)IList<T>
,否则编译器不会知道另一个返回结构体的GetEnumerator
调用,只能使用普通的IEnumerable<T>.GetEnumerator
调用...因此,根据变量的静态类型,您可能会获得不同的性能特征(垃圾回收方面)以及不同的实际行为(可变性/复制)。 - Jon Skeet
List<T>
迭代器 - 我记得有一篇新闻组帖子,其中有一个看起来合理的代码片段,但由于这个设计决策导致了一些非常奇怪的结果 :) 不过我还是会编辑答案...笼统地声称“这不重要”很少是一个好主意。 - Jon Skeet