设计模式之三:迭代器模式(IteratorPattern)

迭代器(Iterator)模式,又叫游标(Cursor)模式。其定义为:提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节。迭代器模式是和容器相关的,对容器对象的访问设计到遍历算法。

迭代器模式由以下角色组成:

  1. 迭代器角色(Iterator):迭代器角色负责定义访问和遍历元素的接口。
  2. 具体迭代器角色(Concrete Iterator):具体迭代器角色要实现迭代器接口,并记录遍历中的当前位置。
  3. 容器角色(Container):容器角色负责提供创建具体迭代器角色的接口。
  4. 具体容器角色(Concrete Container):具体容器角色实现创建具体迭代器角色的接口---这个迭代器角色与该容器的结构相关。

意图:就是提供一种访问集合对象的方法,而又无需暴露该对象的内部细节。

在.NET中的Iterator模式

   在.NET下实现的Iterator模式,对于聚集接口和迭代器接口已经存在了,其中IEnumerator扮演的就是迭代器的角色。它的代码如下:

public interface IEnumerator

    {

        object Current

        {

            get;

        }

        void Reset();

        bool MoveNext();

    }

IEnumerable则扮演的就是抽象聚集的角色,只有一个GetEnumertor()方法。如果集合对象需要具备迭代遍历的功能,就必须实现该接口。

public interface IEnumerable

    {

        IEnumerator GetEnumerator();

    }

下面看一个在网上http://www.theserverside.net/看到的.NET1.1下的迭代器例子,我稍微做了修改,Person类是一个可枚举的类。PersonsEnumerator类是一个枚举器类

public class Persons : IEnumerable

    {

        public string[] m_Names;

        public Persons(string[] names)

        {

            m_Names = new string[names.Length];

            names.CopyTo(m_Names, 0);

        }

        private string this[int index]

        {

            get

            {

                return m_Names[index];

            }

            set

            {

                m_Names[index] = value;

            }

        }

        public IEnumerator GetEnumerator()

        {

            return new PersonsEnumerator(this);

        }

    }

    public class PersonsEnumerator : IEnumerator

    {

        private int index = -1;

        private Persons p;

        public PersonsEnumerator(Persons p)

        {

            this.p = p;

        }

        public bool MoveNext()

        {

            if (index < p.m_Names.Length)

            {

                return true;

            }

            return false;

        }

        public void Reset()

        {

            index = -1;

        }

        public object Current

        {

            get

            {

                return p.m_Names[index];

            }

        }

    }

在.NET2.0及以后的版本,由于有了yield return关键字,实现起来就更简单了:

public class Persons : IEnumerable

    {

        private string[] m_names;

        public Persons(string[] names)

        {

            m_names = new string[names.Length];

            names.CopyTo(m_names,0);

        }

        public IEnumerator GetEnumerator()

        {

            foreach(string name in m_names)

            {

                yield return name;

            }

        }

}

另外顺便简单介绍一下yield关键字:

关键字yield,在迭代器块中用于向枚举数对象提供值或发出迭代结束信号。迭代器有两个特殊语句:yield return <expression>;

                  Yield break;

迭代器块

     迭代器块是有一个或多个yield语句的代码库。下面三种类型的代码块中任意一种都可以是迭代器块:

     方法主体

     访问器主体

     运算符主体

Yield语句只能出现在迭代器块中,该块可用作方法,运算符或访问器的体。这类方法,运算符或访问器的体受以下约束的控制:

  1. 不允许不安全块
  2. 方法,运算符或访问器的参数不能是ref或out
  3. Yield语句不能出现在匿名方法中
  4. Yield return语句不能出在catch块中活含有一个或多个Catch子句的的try块中。

Yield语句的跌代块可以产生IEnumerator和IEnumerable两种对象: