2011-05-17 61 views
4

我试图利用C#中的迭代器来清理我在制作的游戏中的对象上的一些空间查询。使用yield和foreach在无拳击/拆箱的情况下迭代对象的自定义集合

下面是我在做什么目前:

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方法中。我非常想在启动后避免任何分配,所以我没有在关卡或其他东西中触发垃圾回收器。有什么方法可以保持这种语法,同时避免任何分配?也许通过实现我自己的迭代器或什么?

回答

0

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

每个迭代器模块都由编译器使用引用类来实现,以维护迭代过IEnumerable的过程中使用的状态,在您的情况下使用foreach。通常,这个类的分配没有被注意到是一个问题,因为迭代器迭代了一个大集合,或者迭代的客户端正在做他们自己或分配的大量工作,从而压倒了迭代器的分配。

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

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

2

您的代码没有做任何拳击。

只有当您将struct放入object字段时才会发生拳击。
由于您的代码是完全强类型的,并且没有任何object字段,因此它没有框。

调用迭代器方法将创建一个新的迭代器对象(其中implements both IEnumerable<T> and IEnumerator<T>),但不应导致任何其他(幕后)分配。

+0

我明白了。我想也许我的ObjectInfo被迭代器以某种方式塞入到一个对象中。我可以让我自己的迭代器也是一个结构吗?我会查看你提供的链接,看看我能否从中找到答案。 – hahanoob 2011-05-17 01:53:18

+0

看来不可能在没有在幕后分配类对象的情况下使用yield return。那真不幸。 – hahanoob 2011-05-17 02:20:07

+0

@haha:如何在不创建对象的情况下返回'IEnumerable '? – SLaks 2011-05-17 02:38:17