枚举器(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

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