。

Unity中Transform如何实现的枚举器


前言

我使用Unity时,发现Transform这个类可以使用foreach语句遍历其子对象,这是因为其在内部实现了一个枚举器。本文将阐述我关于实现枚举器的理解。


如何实现迭代器?

下方是Microsoft C#开发文档中关于IEnumerator的介绍,示例中为一个类实现了枚举器。

Microsoft Doc IEnumerator 接口

对于这个示例,我的理解是,一个类要想实现枚举器,以使用foreach语句遍历这个非Generics类的集合,需要满足两个条件:

  1. 可以枚举(继承了IEnumerable接口并实现其中的方法)
  2. 制定了一个遍历的规则(有当前的对象,能跳转到下一个对象,能回到最初的位置),即继承了IEnumerator接口并实现其成员

接下来我将进一步输出我的观点。在此之前,我想用自己的话解释UnityEngine.Transform是如何实现可以遍历子对象的Transform组件的枚举器的。

以下来自Unity官方的UnityCsReference仓库:UnityCsReference


public class Transform : Component, IEnumerable
{
    public IEnumerator GetEnumerator()
        {
            return new Transform.Enumerator(this);
        }

        private class Enumerator : IEnumerator
        {
            Transform outer;
            int currentIndex = -1;

            internal Enumerator(Transform outer)
            {
                this.outer = outer;
            }

            //*undocumented*
            public object Current
            {
                get { return outer.GetChild(currentIndex); }
            }

            //*undocumented*
            public bool MoveNext()
            {
                int childCount = outer.childCount;
                return ++currentIndex < childCount;
            }

            //*undocumented*
            public void Reset() { currentIndex = -1; }
        }
    ...
}
          

首先,Transform类继承了Component类和IEnumerable接口,并实现了接口中的方法。继承IEnumerable接口,说明其可以枚举!

然后,其内部实现了一个内部类Enumerator,这个类继承了IEnumerator接口。这说明它定义了枚举器遍历的规则。

内部类中,Transform outer;声明了一个Transform类型的变量,Enumerator的构造函数需要传入一个Transform类型变量,然后将传入的变量赋予outer。
在GetEnumerator()方法中,可以看到,调用了Enumerator的构造函数,传入的参数是Transform这个类的实例本身。说了这么多,意思就是内部类中outer指的就是Transform的一个实例本身。

currentIndex为当前索引,从-1开始,因为枚举器一开始就会调用MoveNext()方法,使得currentIndex变为0。

Current是当前对象,是一个属性,返回的是本Transform的一个实例的索引为currentIndex的子对象的Transform组件

MoveNext()方法中,先获取了当前Transform一个实例拥有的子对象数,再判断当前索引+1后,是否小于子对象总数,如果小于,说明可以继续往下遍历;反之不行,因为超出了索引范围。

Reset()方法能重置索引,当使用foreach遍历完后会自动调用该方法。

可能讲得不是很清楚,后续我会补充……