迭代器(IEnumerator、IEnumerable、yield)

其他相关文章:
https://www.cnblogs.com/zhaopei/p/5769782.html
https://www.cnblogs.com/murongxiaopifu/p/4437432.html
https://www.cnblogs.com/yangecnu/archive/2012/03/17/2402432.html

C#从2.0版本开始提供了更简单的创建枚举器和可枚举类型的方式,实际上编译器将为我们创建它们,这种结构叫迭代器,我们可以把手动编码的可枚举类型和枚举器替换为由迭代器生成的可枚举类型和枚举器。

迭代器块

迭代器块是有一个或多个yield语句的代码块,迭代器块与其他代码块不同,其他块包含的语句被当作是命令式的,也就是说先执行代码块的第一个语句,然后执行后面的语句,最后控制离开块,而迭代器块不需要在同一时间执行一串命令,而是描述了希望编译器为我们创建的枚举器的行为,迭代器块中的代码描述了如何枚举元素

迭代器块有俩个特殊语句:(1)yield return语句指定了序列中返回的下一项 (2)yield break语句指定在序列中没有其他项。

编译器得到有关如何枚举项的描述后,使用它来构建包含所有需要的方法和属性实现的枚举器类,结果类被嵌套包含在迭代器声明的类中如第二张图中这个结果类(嵌套类)实现了IEnumerator<string>,并且被迭代器声明类MyClass所包含

使用迭代器来创建枚举器

BlackAndWhite方法时一个迭代器块,可以为MyClass类产生返回枚举器的方法。

    class MyClass
    {
        public IEnumerator<string> GetEnumerator() { return BlackAndWhite(); }
        public IEnumerator<string> BlackAndWhite() //迭代器
        {
            yield return "black";
            yield return "gray";
            yield return "white";
        }
    }

    public class Program
    {
        static void Main(string[] args)
        {
            MyClass mc = new MyClass();
            foreach (var v in mc)
                Console.WriteLine(v);
        }
    }

使用迭代器来创建可枚举类型

在本例中,BlackAndWrite迭代器方法返回IEnumerble<string>而不是IEnumerator<string>,因此,MyClass首先调用BlackAndWhite方法获取它的可枚举类型对象,然后调用对象的GetEnum方法来获取它的结果,从而实现GetEnumerator方法在Main中。我们可以使用类的实例,也可以调用BlackAndWhite方法,因为它返回的是可枚举类型

   class MyClass
    {
        public IEnumerator<string> GetEnumerator()
        {
            IEnumerable<string> myEnumerable = BlackAndWhite(); //获取可枚举类型
            return myEnumerable.GetEnumerator();  //获取枚举器
        }
        public IEnumerable<string> BlackAndWhite() //返回可枚举类型
        {
            yield return "black";
            yield return "gray";
            yield return "white";
        }
    }

    public class Program
    {
        static void Main(string[] args)
        {
            MyClass mc = new MyClass();
            foreach (var v in mc)   //使用类对象
                Console.WriteLine(v);

            foreach (var v2 in mc.BlackAndWhite())  //使用类枚举器方法
                Console.WriteLine(v2);
        }
    }

俩种迭代器模式

1.当我们实现返回枚举器的迭代器时,必须通过实现GetEnumerator来让类可枚举,它返回由迭代器返回的枚举器如图左部分
2.如果我们在类中实现迭代器返回可枚举类型,可以让类实现GetEnumerator来让类本身可被枚举,或不实现GetEnumerator,让类不可枚举。
   (1)如果实现GetEnumerator,让他调用迭代器方法以获取自动生成的实现IEnumerator的类实例,然后,从IEnumerator对象返回由GetEnumerator创建的枚举器,如图右边所示
   (2)如果通过不实现GetEnumerator使类本身不可枚举,仍然可以使用由迭代器返回的可枚举类只需要直接调用迭代器方法,如右图第二个foreach

将迭代器作为属性

    class Spectrum
    {
        bool _listFromUVtoIR;

        string[] colors = { "violet", "blue", "cyan", "green" };

        public Spectrum(bool listFromUVtoIR)
        {
            _listFromUVtoIR = listFromUVtoIR;
        }

        public IEnumerator<string> GetEnumerator()
        {
            return _listFromUVtoIR ? UVtoIR : IRtoUV;
        }

        //正序
        public IEnumerator<string> UVtoIR
        {
            get
            {
                for (int i = 0; i < colors.Length; i++)
                    yield return colors[i];
            }
        }

        //逆序
        public IEnumerator<string> IRtoUV
        {
            get
            {
                for (int i = colors.Length - 1; i >= 0; i--)
                    yield return colors[i];
            }
        }
    }

    public class Program
    {
        static void Main(string[] args)
        {
            Spectrum startUV = new Spectrum(true);
            Spectrum startIR = new Spectrum(false);

            foreach (string color in startUV)
                Console.Write("{0}", color);
            Console.WriteLine();

            foreach (string color in startIR)
                Console.Write("{0}", color);
            Console.WriteLine();
        }
    }

迭代器实质

在编译器生成枚举器中,Reset方法没有实现,而它是接口需要的方法,因此调用时总是抛出System.NotSupportedException异常,在图18-9中Reset方法显示为灰色,在后台,由编译器生成的枚举器类是包含4个状态的状态机。

1.Before 首次调用MoveNext的初始状态
2.Running 调用MoveNext后进入这个状态,在这个状态中,枚举器检测并设置下一项的位置,在遇到yield return、yield break或在迭代器体结束时,退出状态
3.Suspended 状态机等待下次调用MoveNext的状态
4.After 没有更多项可以枚举

如果状态机在Before或Suspended状态时调用了MoveNext方法,就转到了Running状态,在Running状态中,它检测集合的下一项并设置位置,如果有更多项,状态机会进入Suspended状态,如果没有更多项,它转入并保持在After状态。

 

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