C#的`yield return`给我带来了很多垃圾.可以帮忙吗?

Met*_*ile 6 c# garbage-collection yield-return

我正在使用XNA开发Xbox 360游戏.我真的很想yield return在几个地方使用C#的构造,但它似乎造成了很多垃圾.看看这段代码:

class ComponentPool<T> where T : DrawableGameComponent
    {
    List<T> preallocatedComponents;

    public IEnumerable<T> Components
        {
        get
            {
            foreach (T component in this.preallocatedComponents)
                {
                // Enabled often changes during iteration over Components
                // for example, it's not uncommon for bullet components to get
                // disabled during collision testing
                // sorry I didn't make that clear originally
                if (component.Enabled)
                    {
                    yield return component;
                    }
                }
            }
        }
    ...
Run Code Online (Sandbox Code Playgroud)

到处都使用这些组件池- 用于子弹,敌人,爆炸; 任何无数和短暂的.我经常需要遍历他们的内容,而我只对活跃的组件(即Enabled == true)感兴趣,因此Components属性的行为.

目前,在使用这种技术时,我看到每秒大约800K的额外垃圾.这是可以避免的吗?还有其他方法可以使用yield return吗?

编辑:我发现这个问题关于如何迭代资源池而不创建垃圾的更广泛的问题.许多评论者都不屑一顾,显然不理解Compact Framework的局限性,但这位评论者更加同情并建议创建一个迭代池.这是我将要使用的解决方案.

Ric*_*key 5

编译器对迭代器的实现确实使用了类对象,并且使用(例如,使用 foreach)实现的迭代器yield return确实会导致分配内存。在事情的计划中,这很少是一个问题,因为要么在迭代时完成大量工作,要么在迭代时分配更多内存来做其他事情。

为了使迭代器分配的内存成为问题,您的应用程序必须是数据结构密集型的,并且您的算法必须在不分配任何内存的情况下对对象进行操作。想想类似的生命游戏。突然之间,迭代本身就变得不堪重负。当迭代分配内存时,可以分配大量内存。

如果您的应用程序符合此配置文件(并且仅当),那么您应该遵循的第一条规则是:

  • 当有更简单的迭代概念可用时,避免在内部循环中使用迭代器

例如,如果您有一个类似数组或列表的数据结构,那么您已经公开了索引器属性和计数属性,因此客户端可以简单地使用 for 循环,而不是在迭代器中使用 foreach。这是减少 GC 的“轻松赚钱”,它不会使您的代码变得丑陋或臃肿,只是有点不太优雅。

您应该遵循的第二个原则是:

  • 测量内存分配,看看应该在何时何地使用第一条规则