枚舉器(IEnumerator)、可枚舉類(IEnumerable)的實現來完成遍歷(foreach)

之所以數組可以使用foreach來遍歷裏面的值,原因是數組可以按需提供一個叫做枚舉器的對象,枚舉器可以依次返回請求的數組中的元素,對於有枚舉器的類型而言,獲取一個對象枚舉器的方法時調用對象的GetEnumerator方法,實現GetEnumerator方法的類型叫做可枚舉類型,數組是可枚舉類型。

IEnumerator接口(枚舉器)

實現IEnumerator的枚舉器包含3個函數成員,分別是Current、MoverNext、Reset
(1)Current:返回序列中當前位置的屬性,它是隻讀屬性,它返回object類型的引用,所以可以返回任何類型
(2)MoverNext:是把枚舉器位置前進到集合中下一項的方法,它返回布爾值,指示新的位置是有效位置(返回true),或者是無效位置,比如當前位置到達了尾部(返回false),枚舉器的原始位置在序列中的第一項之前,因此MoveNext必須在第一次使用Current之前調用
(3)Reset:是把位置重置爲原始狀態的方法

自實現foreach函數的功能

using System;
using System.Collections; //使用IEnumerator類型需要添加

namespace lianxi
{
    public class Program
    {
        static void Main(string[] args)
        {
            int[] MyArray = { 10, 11, 12, 13 };
            IEnumerator ie = MyArray.GetEnumerator();  //獲取枚舉器
            while(ie.MoveNext())  //先進行移動,枚舉器原始位置是-1(也就是在集合的第一個元素之前)
            {
                int i = (int)ie.Current;
                Console.WriteLine(i);
            }
        }
    }
}

IEnumerable接口

可枚舉類是指實現了IEnumerable接口的類,IEnumerable接口只有一個成員----GetEnumerator方法,它返回對象的枚舉器

IEnumerable和IEnumerator的使用示例(非泛型)

using System;
using System.Collections; //使用IEnumerator、IEnumerable需要添加

namespace lianxi
{
    class ColorEnumerator:IEnumerator  //實現枚舉器
    {
        string[] _colors;
        int _position = -1;
        public ColorEnumerator(string[] theColors) //構造函數
        {
            _colors = new string[theColors.Length];
            for (int i = 0; i < theColors.Length; i++)
                _colors[i] = theColors[i];
        }

        public object Current   //實現Current
        {
            get
            {
                if (_position == -1)
                    throw new InvalidOperationException();
                if(_position >=_colors.Length)
                    throw new InvalidOperationException();
                return _colors[_position];
            }
        }

        public bool MoveNext()  //實現MoveNext
        {
            if (_position < _colors.Length)
            {
                _position++;
                return true;
            }
            else
                return false;
        }

        public void Reset()     //實現Reset
        {
            _position = -1;
        }
    }

    class Spectrum:IEnumerable   //實現可枚舉類  此處不繼承自IEnumerable也可以foreach遍歷
    {
        string[] Color = { "violet", "blue", "cyan", "green", "yellow", "orange", "red" };
        public IEnumerator GetEnumerator()
        {
            return new ColorEnumerator(Color);
        }
    }
    public class Program
    {
        static void Main(string[] args)
        {
            Spectrum spectrum = new Spectrum();
            foreach (var v in spectrum)
                Console.WriteLine(v);
        }
    }
}

正常我們實現可枚舉類時,都會選擇讓這個自定義的類繼承自IEnumerable,然後按以上代碼的方式來定義,但其實上它不繼承自IEnumerable,也可以實現可枚舉,也可以使用foreach遍歷只要該類定義了GetEnumerator函數,並且該函數返回類型是IEnumerator的就可以滿足條件

    public class A  //無繼承
    {
        public IEnumerator GetEnumerator()  //實現GetEnumerator函數,且返回類型爲IEnumerator
        {
            return new List<int>() { 1,2,3,4,5}.GetEnumerator();
        }
    }
    public class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            foreach (var item in a)
                Console.WriteLine(item);
        }
    }

泛型枚舉接口

在大多數情況下應該使用泛型版本IEnumerable<T>IEnumerator<T>,它與非泛型的區別如下:

泛型接口的枚舉器是類型安全的,它返回實際類型的引用,如果要創建自己的可枚舉類,應該實現這些泛型接口。

using System;
using System.Collections; //使用IEnumerator、IEnumerable需要添加
using System.Collections.Generic; //使用泛型IEnumerator<T>、IEnumerable<T>需要添加

namespace lianxi
{
    class ColorEnumerator:IEnumerator<string>  //實現泛型枚舉器
    {
        private string[] _colors;
        private int _position;

        public ColorEnumerator(string[] theColors) //構造函數
        {
            _position = -1;
            _colors = new string[theColors.Length];
            for (int i = 0; i < theColors.Length; i++)
                _colors[i] = theColors[i];
        }

        public string Current   //實現泛型(IEunmerator<T>)的Current
        {
            get
            {
                if (_position == -1)
                    throw new InvalidOperationException();
                if(_position >=_colors.Length)
                    throw new InvalidOperationException();
                return _colors[_position];
            }
        }

        object IEnumerator.Current  //實現非泛型(IEumerator)的Current
        {
            get
            {
                return this.Current; //返回泛型的Current
            }
        }

        public bool MoveNext()  //實現MoveNext
        {
            if (_position < _colors.Length)
            {
                _position++;
                return true;
            }
            else
                return false;
        }

        public void Reset(){_position = -1;}

        void IDisposable.Dispose() { } //實現泛型的另一個繼承接口中的函數
    }

    class Spectrum:IEnumerable<string>   //實現泛型可枚舉類 不繼承IEnumerable<string> 也可以
    {
        private string[] Color = { "violet", "blue", "cyan", "green", "yellow", "orange", "red" };

        public IEnumerator<string> GetEnumerator()  //實現泛型的GetEnumerator
        {
            return new ColorEnumerator(Color);
        }

        IEnumerator IEnumerable.GetEnumerator()  //實現非泛型的GetEnumerator
        {
            return this.GetEnumerator();
        }
    }
    public class Program
    {
        static void Main(string[] args)
        {
            Spectrum spectrum = new Spectrum();
            foreach (var v in spectrum)
                Console.WriteLine(v);
        }
    }
}

繼承泛型枚舉接口參考自:https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.generic.ienumerable-1?view=netframework-4.8

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章