美文网首页
(转)IEnumerator和IEnumerable详解2

(转)IEnumerator和IEnumerable详解2

作者: aslbutton | 来源:发表于2019-02-27 19:10 被阅读0次

    IEnumerator和IEnumerable

    从名字常来看,IEnumerator是枚举器的意思,IEnumerable是可枚举的意思。
    了解了两个接口代表的含义后,接着看源码:
    IEnumerator:

    public interface IEnumerator
        {
            // Interfaces are not serializable
            // Advances the enumerator to the next element of the enumeration and
            // returns a boolean indicating whether an element is available. Upon
            // creation, an enumerator is conceptually positioned before the first
            // element of the enumeration, and the first call to MoveNext 
            // brings the first element of the enumeration into view.
            // 
            bool MoveNext();
        
            // Returns the current element of the enumeration. The returned value is
            // undefined before the first call to MoveNext and following a
            // call to MoveNext that returned false. Multiple calls to
            // GetCurrent with no intervening calls to MoveNext 
            // will return the same object.
            // 
            Object Current {
                get; 
            }
        
            // Resets the enumerator to the beginning of the enumeration, starting over.
            // The preferred behavior for Reset is to return the exact same enumeration.
            // This means if you modify the underlying collection then call Reset, your
            // IEnumerator will be invalid, just as it would have been if you had called
            // MoveNext or Current.
            //
            void Reset();
        }
    

    IEnumerable:

        public interface IEnumerable
        {
            // Interfaces are not serializable
            // Returns an IEnumerator for this enumerable Object.  The enumerator provides
            // a simple way to access all the contents of a collection.
            [Pure]
            [DispId(-4)]
            IEnumerator GetEnumerator();
        }
    

    发现IEnumerable只有一个GetEnumerator函数,返回值是IEnumerator类型,从注释我们可以得知IEnumerable代表继承此接口的类可以获取一个IEnumerator来实现枚举这个类中包含的集合中的元素的功能(比如List<T>,ArrayList,Dictionary等继承了IEnumeratble接口的类)。

    用foreach来了解IEnumerable,IEnumerator的工作原理

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

    //一个常量的数组,用于foreach遍历
    class ConstArrayList : IEnumerable
    {
        public int[] constItems = new int[] { 1, 2, 3, 4, 5 };
        public IEnumerator GetEnumerator()
        {
            return new ConstArrayListEnumeratorSimple(this);
        }
    }
    //这个常量数组的迭代器
    class ConstArrayListEnumeratorSimple : IEnumerator
    {
        ConstArrayList list;
        int index;
        int currentElement;
        public ConstArrayListEnumeratorSimple(ConstArrayList _list)
        {
            list = _list;
            index = -1;
        }
    
        public object Current
        {
            get
            {
                return currentElement;
            }
        }
    
        public bool MoveNext()
        {
            if(index < list.constItems.Length - 1)
            {
                currentElement = list.constItems[++index];
                return true;
            }
            else
            {
                currentElement = -1;
                return false;
            }
        }
    
        public void Reset()
        {
            index = -1;
        }
    }
    class Program
    {    
        static void Main(string[] args)
        {
            ConstArrayList constArrayList = new ConstArrayList();
            foreach(int item in constArrayList)
            {
                WriteLine(item);
            }
            ReadKey();
        }
    }
    

    输出结果:
    1
    2
    3
    4
    5

    代码达到了遍历效果,但是在用foreach遍历时,IEnumerator和IEnumerable究竟是如何运行的,我们可以通过增加增加日志可以直观的看到原因。

    //一个常量的数组,用于foreach遍历
    class ConstArrayList : IEnumerable
    {
        public int[] constItems = new int[] { 1, 2, 3, 4, 5 };
        public IEnumerator GetEnumerator()
        {
            WriteLine("GetIEnumerator");
            return new ConstArrayListEnumeratorSimple(this);
        }
    }
    //这个常量数组的迭代器
    class ConstArrayListEnumeratorSimple : IEnumerator
    {
        ConstArrayList list;
        int index;
        int currentElement;
        public ConstArrayListEnumeratorSimple(ConstArrayList _list)
        {
            list = _list;
            index = -1;
        }
    
        public object Current
        {
            get
            {
                WriteLine("Current");
                return currentElement;
            }
        }
    
        public bool MoveNext()
        {
            if(index < list.constItems.Length - 1)
            {
                WriteLine("MoveNext true");   
                currentElement = list.constItems[++index];
                return true;
            }
            else
            {
                WriteLine("MoveNext false");
                currentElement = -1;
                return false;
            }
        }
    
        public void Reset()
        {
            WriteLine("Reset");
            index = -1;
        }
    }
    class Program
    {    
        static void Main(string[] args)
        {
            ConstArrayList constArrayList = new ConstArrayList();
            foreach(int item in constArrayList)
            {
                WriteLine(item);
            }
            ReadKey();
        }
    }
    

    输出结果:
    GetIEnumerator
    MoveNext true
    Current
    1
    MoveNext true
    Current
    2
    MoveNext true
    Current
    3
    MoveNext true
    Current
    4
    MoveNext true
    Current
    5
    MoveNext false

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

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

    其次我们发现:

    ConstArrayList constArrayList = new ConstArrayList();
    foreach(int item in constArrayList)
    {
        writeLine(item);
    }
    

    其实就等价于:

    ConstArrayList constArrayList = new ConstArrayList();
    IEnumerator enumeratorSimple = constArrayList.GetEnumerator();
    while (enumeratorSimple.MoveNext())
    {
        int item = (int)enumeratorSimple.Current;
        WriteLine(item);
    }
    

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

    相关文章

      网友评论

          本文标题:(转)IEnumerator和IEnumerable详解2

          本文链接:https://www.haomeiwen.com/subject/fugfuqtx.html