IEnumerable和IEnumerator详解

组成

从名字常来看,IEnumerator是枚举器的意思,IEnumerable是可枚举的意思。这两个都是个接口

接下来我们看一下IEnumerator和IEnumerable的源码

 public interface IEnumerable
    {
        IEnumerator GetEnumerator();
    }

IEnumerable非常简单,就一个GetEnumerator()方法,但是这个方法的返回值却是一个IEnumerator对象,从注释我们可以得知IEnumerable代表继承此接口的类可以获取一个IEnumerator来实现枚举这个类中包含的集合中的元素的功能(比如List<T>,ArrayList,Dictionary等继承了IEnumeratble接口的类,接下来我们看下IEnumerator这个接口中有什么。


    public interface IEnumerator
    {
        object Current { get; }
        bool MoveNext();
        void Reset();
    }

IEnumerator有一个object类型的属性,还有一个返回值为bool值的MoveNext方法和一个无返回值的Reset方法

实现接口

我们模仿ArrayList来实现一个简单的MyList,然后用foreach遍历。

public class MyList : IEnumerable
{
    public int[] _data = new int[10] { 1, 5, 7, 9, 7, 8, 7, 8, 7, 4 };

    public int this[int index]
    {
        get
        {
            return _data[index];
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        Debug.Log("foreach调用  GetEnumerator");
        return new MIEnumtor(this);
    }
}

我们的MyList提供了一个数组用来遍历,然后提供一个索引器取得里面的元素,然后我们实现IEnumerable这个接口的GetEnumerator方法。

public class MIEnumtor : IEnumerator
{
    private MyList myList;

    private int index;

   public  MIEnumtor(MyList my)
    {
        index = -1;
        myList = my;
    }

    public object Current
    {
        get
        {
            Debug.Log("foreach调用  Current");
            return myList[index];
        }
    }

    public bool MoveNext()
    {
        Debug.Log("foreach调用  MoveNext");
        if (index < myList._data.Length - 1)
        {
            index++;
            return true;
        }
        index = -1;
        return false;
    }

    public void Reset()
    {

    }
}

然后我们提供一个实现了IEnumerator接口的MIEnumtor 类,我们定义了一个索引index用来指向当前集合的元素位置,在属性Current中我们返回index位置的元素,每调用一次MoveNext我们就将索引向下移动,知道集合的最后一个元素时,重置索引并返回false,然后我们用Foreach来遍历这个集合。

public class Test2 : MonoBehaviour
{
    private void Awake()
    {
        MyList my = new MyList();

        foreach (var item in my)
        {
            Debug.Log(item);
        }
    }
}

最后我们的日志输出了如下结果

分析

通过输出结果,我们可以发现,foreach在运行时会先调用MyList的GetIEnumerator函数获取一个MIEnumtor,之后通过循环调用MIEnumtor的MoveNext函数,index后移,更新Current属性,然后返回Current属性,直到MoveNext返回false。

总结

GetIEnumerator()负责获取枚举器。
MoveNext()负责让Current获取下一个值,并判断遍历是否结束。
Current负责返回当前指向的值。
Rest()负责重置枚举器的状态(在foreach中没有用到)
这些就是IEnumerable,IEnumerator的基本工作原理了。

其次我们发现

        MyList my = new MyList();

        foreach (var item in my)
        {
            Debug.Log(item);
        }

其实就等价于

        MyList my = new MyList();

        MIEnumtor mIEnumtor = my.GetEnumerator();

        while (mIEnumtor.MoveNext())
        {
            Debug.Log(mIEnumtor.Current);
        }

也就是说foreach其实是一种语法糖,用来简化对可枚举元素的遍历代码。而被遍历的类通过实现IEnumerable接口和一个相关的IEnumerator枚举器来实现遍历功能。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值