C# 使用yield和foreach在自定义对象集合上迭代,而无需装箱/取消装箱

C# 使用yield和foreach在自定义对象集合上迭代,而无需装箱/取消装箱,c#,iterator,foreach,yield,yield-return,C#,Iterator,Foreach,Yield,Yield Return,我试图利用C#中的迭代器来清理我正在制作的游戏中对象的一些空间查询 以下是我目前正在做的事情: public struct ObjectInfo { public int x, y; public int Type; public int hp; } public static IEnumerable<ObjectInfo> NearbyObjects(int x, int y, int distanc

我试图利用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;
        }
    }
public结构ObjectInfo
{
公共整数x,y;
公共int类型;
公共int hp;
}
公共静态IEnumerable NearbyObjects(整数x、整数y、整数距离)
{
//其实不是我在做什么,但为了简单。。。
对于(int i=0;i<10;++i)
{
返回新的ObjectInfo();
}
}
公共静态空间爆炸()
{
foreach(NearbyObjects(0,0,1)中的ObjectInfo o)
{
o、 hp=0;
}
}

这非常有效,但我认为有一些装箱/拆箱正在进行。当我在Explode方法中看到分配时,CLRProfiler似乎验证了这一点。我很想避免启动后的任何分配,这样我就不会在某个级别或某个东西中触发垃圾收集器。有没有什么方法可以在避免任何分配的同时保持这种语法?也许通过实现我自己的迭代器或其他什么?

将对结构进行装箱/取消装箱

请看这里:


同样在这里:

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

装箱仅在将
结构
放入
对象
字段时发生。
因为您的代码是完全强类型的,并且没有任何
对象
字段,所以它不会装箱


调用迭代器方法将创建一个新的迭代器对象(它),但不应导致任何其他(幕后)分配。

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

编译器使用引用类实现每个迭代器块,以维护在迭代
IEnumerable
过程中使用的状态,在使用
foreach
的情况下。通常不会注意到此类的分配是一个问题,因为迭代器会迭代一个大型集合,或者正在迭代的客户机正在做大量自己的工作,或者分配会压倒迭代器的分配

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


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

我明白了。我想也许迭代器不知怎么把我的ObjectInfo塞进了一个对象中。我能做我自己的迭代器吗?那也是一个结构?我将查看您提供的链接,看看是否可以从中找到答案。如果不在幕后分配类对象,似乎不可能使用yield return。这太不幸了。@haha:如果不创建对象,你怎么能返回一个
IEnumerable
?不过我不一定需要返回一个实现IEnumerable的类对象?我可以用GetEnumerator方法返回一个结构。似乎是收益率回报产生的状态机导致了分配。@haha:那没用。由于该方法返回
IEnumerable
(不是私有结构类型),因此需要将其装箱。