• 二十三种设计模式[16]


    前言

           迭代器模式,属于对象行为型模式。它的目的是将一个集合对象的迭代与其本身分离,使这个聚合对象更单纯,并且在遍历的同时不需要暴露该聚合对象的内部结构。

           在《设计模式 - 可复用的面向对象软件》一书中将之描述为“ 提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露对象的内部表示 ”。

    结构

    Interator_1

    • Aggregate(聚合对象接口):负责定义创建相应迭代器对象的接口;
    • ConcreteAggregate(具体聚合对象):具体的聚合对象,实现具体迭代器对象的创建;
    • Iterator(迭代器接口):定义对聚合对象的访问和遍历的接口;
    • ConcreteIterator(具体迭代器):实现对聚合对象的访问和遍历,并记录当前遍历的位置;

    示例

    Interator_2

    public interface Iterator
    {
        object Current { get; }
        bool Next();
        void Reset();
    }
    
    public class ConcreteIterator : Iterator
    {
        private int Index { set; get; } = -1;
        private int[] IntArr;
        public ConcreteIterator(int[] intArr)
        {
            this.IntArr = intArr;
        }
    
        public object Current
        {
            get
            {
                if(this.IntArr == null
                    || this.IntArr.Length < 1
                    || this.Index < 0
                    || this.Index >= this.IntArr.Length)
                {
                    return null;
                }
    
                return this.IntArr[this.Index];
            }
        }
    
        public bool Next()
        {
            this.Index++;
            return this.Index < this.IntArr.Length;
        }
    
        public void Reset()
        {
            this.Index = -1;
        }
    }
    
    public interface Aggregate
    {
        Iterator CreateIterator();
    }
    
    public class ConcreteAggregate : Aggregate
    {
        private int[] IntArr;
        public ConcreteAggregate()
        {
            this.IntArr = new int[]{1, 2, 3, 4, 5, 6, 7};
        }
        public Iterator CreateIterator()
        {
            return new ConcreteIterator(this.IntArr);
        }
    }
    
    static void Main(string[] args)
    {
        Aggregate agg = new ConcreteAggregate();
        Iterator.Iterator iterator = agg.CreateIterator();
    
        while (iterator.Next())
        {
            Console.WriteLine(iterator.Current.ToString());
        }
    
        Console.ReadKey();
    }

           在上述示例中,由ConcreteAggregate类提供了一个工厂方法(Factory Method)来返回它的迭代器。迭代器的公共接口Iterator保证了各个迭代器的一致性,当我们需要变更ConcreteAggregate的迭代操作时,只需要增加一个迭代器并修改CreateIterator函数中返回的迭代器即可。迭代器的存在使聚合的内部结构对调用者透明,同时又统一了各个聚合结构的外部迭代方式(无论那种聚合结构,调用者只需要使用Next函数和Current属性即可完成迭代)。

    迭代器在C#中的应用

           我们在使用C#语言开发时经常会使用关键字foreach来遍历一个集合。一个类型若要支持使用foreach来遍历,需要满足以下两点:

      1. 该类型具有公共无参数的方法GetEnumerator,并且其返回值类型为类、接口或结构;
      2. 函数GetEnumerator的返回值类型中必须包含公共属性Current和公共无参且返回值为bool类型的函数MoveNext;

           foreach语法可参照:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/foreach-in

           在使用foreach遍历一个类型时,首先会调用这个类型的GetEnumerator函数来获得一个对象,之后调用这个对象的MoveNext函数,该函数返回true则进入循环内并将属性Current的值赋给foreach中定义的变量,反之则跳出循环。可参照如下代码。

    public class ClassA
    {
        public int[] IntArr { set; get; } = new int[] { 1, 2, 3, 4, 5, 6, 7 };
        public ClassB GetEnumerator()
        {
            Console.WriteLine("ClassA.GetEnumerator");
            Console.WriteLine("------------------------");
            return new ClassB(this);
        }
    }
    
    public class ClassB
    {
        private int Index { set; get; } = -1;
        private ClassA ClassA { set; get; }
    
        public ClassB(ClassA classA)
        {
            this.ClassA = classA;
        }
    
        public int Current
        {
            get
            {
                Console.WriteLine($"[ClassB.Current]:{this.ClassA.IntArr[this.Index]}");
                return this.ClassA.IntArr[this.Index];
            }
        }
    
        public bool MoveNext()
        {
            this.Index++;
            Console.WriteLine($"[ClassB.MoveNext]:{this.Index < this.ClassA.IntArr.Length}");
            return this.Index < this.ClassA.IntArr.Length;
        }
    }
    
    static void Main(string[] args)
    {
        ClassA classA = new ClassA();
        foreach (var item in classA)
        {
            Console.WriteLine($"-------{item.ToString()}");
        }
        Console.ReadKey();
    }

    image

           ClassA类中的函数GetEnumerator所返回的实际上就是一个迭代器,通过这个迭代器中的函数MoveNext与属性Current来遍历集合。效果与如下代码相同。

    static void Main(string[] args)
    {
        ClassB classB = new ClassB(new ClassA());
        while (classB.MoveNext())
        {
            Console.WriteLine($"-------{classB.Current.ToString()}");
        }
        Console.ReadKey();
    }

           终上所述,关键字foreach实际上是一个方便我们使用迭代器模式去遍历一个对象的语法糖,也就意味着所有支持foreach的类型都采用了迭代器模式。而C#也为我们提供了迭代器模式中的聚合对象接口IEnumerable以及迭代器接口IEnumerator。在接口IEnumerable中只定义了一个返回IEnumerator的函数GetEnumerator,而在Ienumerator接口中则定义了属性Currnet、MoveNext和Reset函数。

    public class List<T> : IList<T>, System.Collections.IList, IReadOnlyList<T>
    {
        ...
        ...
        public Enumerator GetEnumerator() {
            return new Enumerator(this);
        }
    
        /// <internalonly/>
        IEnumerator<T> IEnumerable<T>.GetEnumerator() {
            return new Enumerator(this);
        }
    
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
            return new Enumerator(this);
        }
        ...
        ...
        ...
    }
    
    public struct Enumerator : IEnumerator<T>, System.Collections.IEnumerator
    {
        private List<T> list;
        private int index;
        private int version;
        private T current;
    
        internal Enumerator(List<T> list) {
            this.list = list;
            index = 0;
            version = list._version;
            current = default(T);
        }
    
        public void Dispose() {
        }
    
        public bool MoveNext() {
    
            List<T> localList = list;
    
            if (version == localList._version && ((uint)index < (uint)localList._size)) 
            {                                                     
                current = localList._items[index];                    
                index++;
                return true;
            }
            return MoveNextRare();
        }
    
        private bool MoveNextRare()
        {                
            if (version != list._version) {
                ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
            }
    
            index = list._size + 1;
            current = default(T);
            return false;                
        }
    
        public T Current {
            get {
                return current;
            }
        }
    
        Object System.Collections.IEnumerator.Current {
            get {
                if( index == 0 || index == list._size + 1) {
                     ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
                }
                return Current;
            }
        }
    
        void System.Collections.IEnumerator.Reset() {
            if (version != list._version) {
                ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
            }
            
            index = 0;
            current = default(T);
        }
    
    }

           以上为.NET Framework 4.6.2的部分List类源码,有兴趣可以自行下载(https://referencesource.microsoft.com/

    总结

           迭代器模式能够帮助我们将集合类型的迭代逻辑与类型本身分离,使得该类型的底层结构对调用者透明,并且能够使调用者使用相同的方式(MoveNext和Current)去遍历不同的集合类型(List、数组)或使用同一集合类型以及同一方式去执行不同的迭代逻辑。但迭代器的个数随着迭代逻辑的增加而增加,迭代器的数量越多,系统的复杂性越高。

           需要注意的是,无论是否使用迭代器模式,都不能在遍历的过程中对该集合进行增加或删除操作(在.NET源码中采用版本号_version来判断是否在遍历的过程中操作过该集合)。

           以上,就是我对迭代器模式的理解,希望对你有所帮助。

           示例源码:https://gitee.com/wxingChen/DesignPatternsPractice

           系列汇总:https://www.cnblogs.com/wxingchen/p/10031592.html

           本文著作权归本人所有,如需转载请标明本文链接(https://www.cnblogs.com/wxingchen/p/10078501.html)

  • 相关阅读:
    兼容css3.0中的boxshadow
    获取页面和元素可视高度
    关于javascript中apply()和call()方法的区别
    BFC(Block Formatting Context)
    minheight最小高度的实现(兼容IE6、IE7、FF)
    sicily 1825. Nickname
    sicily 2000. Toy Shopping
    sicily 2075. 2.2 Computing the volume of a cylinder
    sicily 2001. Scavenger Hunt
    sicily 1608. Digit Counting
  • 原文地址:https://www.cnblogs.com/wxingchen/p/10078501.html
Copyright © 2020-2023  润新知