纵谈迭代模式与IEnumerable

漫谈迭代模式与IEnumerable

迭代模式与IEnumerable

一个类型要实现foreach遍历就必须先实现IEnumerable接口和IEnumerator接口,IEnumerable接口和IEnumerator接口的工作原理也正是设计模式中迭代模式最好的例子。在理解IEnumerable接口和IEnumerator接口之前,先来理解迭代模式是如何实现的,对理解IEnumerable接口和IEnumerator接口的工作原理很有帮助。

Aggregate:聚合

Iterator:迭代器

Client:客户

纵谈迭代模式与IEnumerable

如图所示,聚合(Aggregate)迭代器(Iterator)分别为抽象的接口,通过具体的实现,具体的聚合(ConcreteAggregate)与具体的迭代器(ConcreteIterator)分别建立了依赖关系和聚合关系,实现迭代器(Iterator)通过客户(Client)聚合(Aggregate)的迭代操作。

OO版迭代模式

客户类(Client)

客户类(Client):是建立迭代模式的具体载体,最终是实现迭代器对聚合的客户类进行顺序迭代输出

public class Client
    {
        public string Name { get; set; }
        public override string ToString()
        {
            return Name;
        }
    }

聚合(Aggregate):抽象聚合接口(Aggregate)创建一个用于查询自己的迭代器,它不关心自己存储的聚合元素,也不关心指定的迭代器是谁,因为那是具体的聚合类,而具体的聚合类(ConcreteAggregate)定义一个具体的客户类(Client)集合,同时实现了CreatedIterator()创建自己指定的迭代器(依赖关系0,便于检索也定义了索引器,Index属性,Count属性,Add方法用于添加集合元素操作。

聚合接口(Aggregate)

 public interface Aggregate
    {
        Iterator CreatedIterator();
   }

具体的聚合类(ConcreteAggregate)实现聚合接口(Aggregate)

 public class ConcreteAggregate : Aggregate
    {
        //线性结构存储
        protected List<Client> ClientList = new List<Client>();
        //通过索引器以只读的方式将数据结构对外公开
        public Client this[int index]
        {
            get
            {
                return ClientList[index];
            }
        }

        public int Index { get; set; }
        public int Count
        {
            get
            {
                return ClientList.Count;
            }
        }

        public void Add(Client client)
        {
            ClientList.Add(client);
        }

        //指定所用的索引器,与索引器之间建立依赖关系
        public Iterator CreatedIterator()
        {
            return new ConcreteIterator(this);
        }
    }

迭代器(Iterator)

迭代器的任务是查找出所要查找的集合元素,下一个元素Next(),当前元素Current(),重置元素Reset(),它也不关心将要查找哪个集合,具体实现的迭代器类(ConcreteIterator)则要负责实现Next(),Current(),Reset()成员,同时建立了与聚合类的关联关系,注意,在构造函数中对关联的集合类进行初始化操作,也正是这样聚合类才和迭代器形成依赖关系,注意依赖关系和关联关系是如何用代码的方式实现的

迭代器接口(Iterator)

public interface Iterator
    {
        bool Next();
        Client Current();
        void Reset();
    }

具体的迭代器类(ConcreteIterator)实现迭代器接口(Iterator)

public class ConcreteIterator : Iterator
    {
        //与聚合类形成关联关系
        ConcreteAggregate Agg = null;
        public ConcreteIterator(ConcreteAggregate sch)
        {
             //指定所要查询的聚合类
            Agg = sch;
            Agg.Index = -1;
        }
        /// <summary>
        /// Next()为指向下一个对象并
        /// 判断是否到达集合的末尾
        /// </summary>
        public bool Next()
        {
          // 当聚合类的索引不等于集合长度时,继续向下查找,返回true并加一操作,否则返回false表示查询已结束
            return (++Agg.Index != Agg.Count) ? true : false;
        }

        /// <summary>
        /// Current()利用索引器取出当前集合元素
        /// </summary>
        public Client Current()
        {
            return Agg[Agg.Index];
        }

        /// <summary>
        /// 重置下标
        /// </summary>
        public void Reset()
        {
            Agg.Index = -1;
        }
    }

主函数调用

 static void Main(string[] args)
        {
            //建立聚合类并添加聚合元素
            ConcreteAggregate aggregate = new ConcreteAggregate();
            aggregate.Add(new Client() { Name = "ABC" });
            aggregate.Add(new Client() { Name = "CBA" });
            aggregate.Add(new Client() { Name = "EEELab" });

            //取出聚合类指定的迭代器,并对迭代器进行操作
            Iterator iter = aggregate.CreatedIterator();
            while (iter.Next())
            {
                Console.WriteLine(iter.Current());
            }
        }

IEnumerable接口和IEnumerator接口

在理解了前面的迭代模式后,在理解下面的IEnumerable接口和IEnumerator接口实现工作原理也就不成问题。你会发现这两个接口的实现正是一个典型的迭代模式。

客户类(Client)

 public class Client
    {
        public string Name { get; set; }
        public override string ToString()
        {
            return Name;
        }
    }

聚合类(ConcreteAggregate)实现IEnumerable接口

public class ConcreteAggregate : System.Collections.IEnumerable
    {
        protected List<Client> ClientList = new List<Client>();
        public int Index { get; set; }
        public int Count
        {
            get
            {
                return ClientList.Count;
            }
        }
        public Client  this[int index]
        {
            get
            {
                return ClientList[index];
            }
        }
        public void Add(Client client)
        {
            ClientList.Add(client);
        }
        //对应于OO版本的CreatedIterator()方法
        public System.Collections.IEnumerator GetEnumerator()
        {
            return new ConcreteIterator(this);
        }
    }

具体的迭代器类(ConcreteIterator)实现IEnumerator接口

public class ConcreteIterator : System.Collections.IEnumerator
    {
        ConcreteAggregate school = null;
        public ConcreteIterator(ConcreteAggregate sch)
        {
            school = sch;
            school.Index = -1;
        }

        //对应OO版本的Current()
        object System.Collections.IEnumerator.Current
        {
            get { return school[school.Index]; }
        }
        //对应OO版本的Next()
        public bool MoveNext()
        {
            school.Index++;
            return (school.Index != school.Count) ? true : false;
        }
        //对应OO版本的Reset()
        public void Reset()
        {
            school.Index = -1;
        }
    }

主函数调用

static void Main(string[] args)
        {
            //建立聚合类并添加聚合元素
            ConcreteAggregate aggregate = new ConcreteAggregate();
            aggregate.Add(new Client() { Name = "ABC" });
            aggregate.Add(new Client() { Name = "CBA" });
            aggregate.Add(new Client() { Name = "EEELab" });
            //取出聚合类指定的迭代器,并对迭代器进行操作
            IEnumerator Ienumerator = aggregate.GetEnumerator();
            while (Ienumerator.MoveNext())
            {
                Console.WriteLine(Ienumerator.Current);
            }
        }

你会发现,IEnumerable接口和IEnumerator接口分别对应着聚合接口(Aggregate)迭代器接口(Iterator),因此,实现IEnumerable接口和IEnumerator接口是实现迭代模式的典型例子,对理解如何让自己定义的类能够实现foreach遍历来说很有意义。

在主函数中用foreach调用聚合类(ConcreteAggregate)

static void Main(string[] args)
        {
            ConcreteAggregate aggregate = new ConcreteAggregate();
            aggregate.Add(new Client() { Name = "ABC" });
            aggregate.Add(new Client() { Name = "CBA" });
            aggregate.Add(new Client() { Name = "EEELab" });

            System.Collections.IEnumerator Ienumerator = aggregate.GetEnumerator();
            while (Ienumerator.MoveNext())
            {
                Console.WriteLine(Ienumerator.Current);
            }

            foreach (Client item in aggregate)
            {
                Console.WriteLine(item.Name);
            }
        }

之所以foreach要用继承于IEnumerable接口的索引聚合类(ConcreteAggregate)是因为foreach直接调用索引集合的所继承IEnumerator接口的Current属性和MoveNext()成员方法。

下面是foreachCIL代码,从代码可以看出foreach是在内部隐式的执行循环指令来实现我们在迭代模式中的循环操作,因此,从根本上说,执行foreach操作就是一个迭代模式的执行过程。

纵谈迭代模式与IEnumerable

foreach的CIL代码

.try

  {

    IL_0087:  br.s       IL_00a4  //跳转到执行MoveNext()的上一行

    IL_0089:  ldloc.s    CS$5$0001

    IL_008b:  callvirt   instance object [mscorlib]System.Collections.IEnumerator::get_Current()   //Current属性

    IL_0090:  castclass  IteratorNET.Client

    IL_0095:  stloc.2

    IL_0096:  nop

    IL_0097:  ldloc.2

    IL_0098:  callvirt   instance string IteratorNET.Client::get_Name()//Name属性

    IL_009d:  call       void [mscorlib]System.Console::WriteLine(string)

    IL_00a2:  nop

    IL_00a3:  nop

    IL_00a4:  ldloc.s    CS$5$0001

    IL_00a6:  callvirt   instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()

    IL_00ab:  stloc.s    CS$4$0000

    IL_00ad:  ldloc.s    CS$4$0000

    IL_00af:  brtrue.s   IL_0089  //跳转回取Current属性的上一行

    IL_00b1:  leave.s    IL_00d0

  }  // end .try

  finally

  {

    IL_00b3:  ldloc.s    CS$5$0001

    IL_00b5:  isinst     [mscorlib]System.IDisposable

    IL_00ba:  stloc.s    CS$0$0002

    IL_00bc:  ldloc.s    CS$0$0002

    IL_00be:  ldnull

    IL_00bf:  ceq

    IL_00c1:  stloc.s    CS$4$0000

    IL_00c3:  ldloc.s    CS$4$0000

    IL_00c5:  brtrue.s   IL_00cf

    IL_00c7:  ldloc.s    CS$0$0002

    IL_00c9:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()

    IL_00ce:  nop

    IL_00cf:  endfinally

  }  // end handler