迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示。C#中使用IEnumerator接口实现,Java中使用Iterator接口实现,其中原理都差不多,下面我就用C#代码来演示下迭代器的实现。
假如我要实现一个自定义容器列表,就叫它SpecialList吧,可以实现类似ArrayList的功能。如下:
这个时候我想遍历这个列表,当我使用foreach的时候,报错了。如下:
提示SpecialList不包含GetEnumerator的公共实例定义,在C#中,如果想要使用foreach遍历列表对象,这个列表对象必须要实现IEnumerable的GetEnumerator()方法,我们来加一下。如下:
继承后,foreach列表的时候就不会再报错了,但我们并没有实现GetEnumerator()这个方法。我们看一下这个方法的返回值类型为IEumerator,F12进去看一下这个接口的定义。如下:
这个接口里面有两个方法和一个属性。其中MoveNext()是最核心的方法,就是在这个里面实现列表的遍历。话不多说,我们先定义一个继承IEnumerator的类,就叫SpecialEnumerator吧。如下:
不好意思,我已经简单的写好这个类了,那就简单的讲一个这个类的原理吧。SpecialEnumerator主要定义了一个SpecialList类型的_list,还有当前元素的_current,还有当前索引的_index。我们重点看一下MoveNext方法,里面的代码其实也非常简单,就是判断_index是否已经到底,没有的话叫取出当前_index上的元素赋予_cuurent,然后返回true,这里的返回值给到foreach,如果为true,则继续遍历,如果为false,则foreach跳出循环。我们再来看一下SpecialList中怎么使用SpecialEnumerator类。如下:
这样只是返回SpecialEnumerator的一个实例,同时将当前要枚举的列表对象(this)传入进去。你可能会有疑问,这里只是返回一个对象,在foreach中并没有循环判断MoveNext是true还是false的代码,那它是怎么实现的呢?其实foreach只是一个语法糖,它本质上其实是使用了while语法来实现的。如下:
到这里,相信大家已经明白迭代器是怎么实现的了。最后再补充一下,大家可能会觉得额外实现SpecialEnumerator类比较繁琐,其实如果你的列表遍历规则比较简单的话,可以使用yield语法来代替SpecialEnumerator类,只要这样写就行。如下:
但如果你的列表遍历规则比较特殊,比如像栈、队列这样的容器类时,还是建议实现枚举类,这样可以将遍历规则封装在MoveNext中。