使用收益和foreach迭代自定义对象集合,而无需装箱/取消装箱

本文关键字:取消装箱 集合 收益 foreach 迭代 对象 自定义 | 更新日期: 2023-09-27 17:56:43

我正在尝试利用 C# 中的迭代器来清理我正在制作的游戏中对对象的一些空间查询。

这是我目前正在做的事情:

    public struct ObjectInfo
    {
        public int x, y;
        public int Type;
        public int hp;
    }
    public static IEnumerable<ObjectInfo> NearbyObjects(int x, int y, int distance)
    {
        // Not actually what I'm doing, but for simplicity...
        for (int i = 0; i < 10; ++ i)
        {
            yield return new ObjectInfo();
        }
    }
    public static void Explode()
    {
        foreach (ObjectInfo o in NearbyObjects(0, 0, 1))
        {
            o.hp = 0;
        }
    }

这很好用,除了我认为有一些装箱/拆箱正在进行。CLRProfiler 似乎验证了这一点,因为我看到我的 Explode 方法中发生了分配。我非常想在启动后避免任何分配,所以我不会在关卡或其他东西中间触发垃圾收集器。有没有办法在避免任何分配的同时保留此语法?也许通过实现我自己的迭代器或其他东西?

使用收益和foreach迭代自定义对象集合,而无需装箱/取消装箱

您的代码不执行任何装箱操作。

只有当你把struct放进object场时,才会发生拳击。
由于您的代码是完全强类型的,并且没有任何object字段,因此它不会装箱。

调用迭代器方法将创建一个新的迭代器对象(实现IEnumerable<T>IEnumerator<T>),但不会导致任何其他(后台)分配。

装箱/拆箱将发生在结构体中。

请参阅此处: http://msdn.microsoft.com/en-us/library/aa664476(v=vs.71).aspx

同样在这里:将结构转换为通用接口时是否有装箱/拆箱?

Explode方法中的分配是由于迭代器本身的内部工作,而不是由于装箱。

每个迭代器块都由编译器使用引用类来实现,以维护在迭代IEnumerable过程中使用的状态,在您使用foreach的情况下。 通常,此类的分配不会被视为问题,因为迭代器迭代大型集合,或者正在迭代的客户端正在执行自己的大量工作或分配,这些工作使迭代器分配不堪重负。

但是,如果你的集合很小(就像你的一样),并且foreach非常快并且没有自己的分配,那么剩下的就是迭代器状态类的分配。

要解决此性能问题,您可以简单地重新组织代码以使用for而不是foreach探查器告诉您在迭代器块中分配了大量内存。 这通常是一个适度的变化,如果可衡量,性能提升是值得的。