• C# 枚举器和迭代器


    一、枚举器(enumerator)和可枚举类型(enumeration)

      我们都知道foreach语句可以用来遍历数组中的元素,但你有没有想过为什么它可以被foreach处理呢?

      这是因为数组可以按需提供一个叫做枚举器的对象,枚举器“知道”项的次序并且跟踪它在序列中的位置,然后返回请求的当前项,所以可以依次遍历数组

      你可以通过调用对象的GetEnumerator方法来获取它的枚举器,实现了GetEnumerator方法的类型叫做可枚举类型,毫无疑问,数组是可枚举类型

    二、IEnumerator接口和IEnumerable接口

     1.IEnumerator接口

      实现了IEnumerator接口的枚举器包含三个函数成员

        a: Current  它是返回序列中当前位置项的属性,并且是只读的,返回object类型的引用

        b: MoveNext()  它是把枚举器位置前进到集合中下一项的方法,返回布尔值用来指示下一项的位置是否有效,有效返回true,否则返回false

                   需要注意的是,枚举器的原始位置在第一项之前(下标从0开始的话,也可以理解为-1的位置),因此MoveNext()必须在第一次使用Current之前调用

        c: Reset()  把位置重置为原始状态的方法

      让我们通过模仿foreach演示一下如何使用

    using System;
    using System.Collections;
    using System.Collections.Generic;
    
    namespace 枚举器
    {
        class Program
        {
            static void Main(string[] args)
            {
                int[] arr = { 1, 3, 5, 7 };
                IEnumerator ie = arr.GetEnumerator();   //获取枚举器
                while (ie.MoveNext())   //移动到下一项
                {
                    Console.Write("{0} ", ie.Current);      //获取当前项的值
                }
                ie.Reset();     //重置
                Console.ReadKey();
            }
        }
    }

    和foreach结果一样,为:  1 3 5 7

    扩展一点,foreach遍历可枚举对象时,它会调用GetEnumerator方法获取对象的枚举器,然后再从枚举器中申请每一项作为迭代变量,代码可以读取该变量,但不能改变它

     2.IEnumerable接口

      可枚举类型是指实现了IEnumerable接口的类。IEnumerable接口只有一个成员---------GetEnumerator方法,它返回对象的枚举器

    class MyColor : IEnumerable
            {
                string[] color = { "red", "green", "yellow" };
                public IEnumerable GetEnumerator()
                {
                    return new ColorEnum(color);    //ColorEnum()为枚举器类的实例
                }
            }

    三、泛型枚举接口

      前面的枚举接口都是非泛型版本,实际上大多数情况下都应该使用泛型版本IEnumerable<T>和IEnumerator<T>(关于泛型你可以看一下我之前写过的博客),介绍一下它们之间的差别

    a: 非泛型接口形式

      IEnumerable接口的GetEnumerator方法返回的是IEnumerator枚举器类的实例;

      实现IEnumerator的类实现了Current属性,它返回object的引用,我们必须把它转换成实际类型的对象

    b: 泛型接口形式

      IEnumerable<T>接口的GetEnumerator方法返回的是IEnumerator<T>枚举器类的实例;

      实现IEnumerator<T>的类实现了Current属性,它返回实际类型的引用,而不是object基类的引用

    需要注意的是,我们目前所看到的非泛型接口的枚举类型是不安全的,它们返回object类型的引用必须转化为实际类型,而泛型接口的枚举类型是安全的,它返回实际类型的引用

    四、迭代器

      有米有觉得创建枚举器和可枚举类型有点子复杂?C# 2.0 开始提供了更简单的创建方式,实际上编译器会为我们创建它们,它J就是迭代器(iterator),先看代码,它实现了一个产生和返回枚举器的迭代器

    public IEnumerator<string> Colors()
            {
                string[] theColors = { "black", "white", "gray" };
                for (int i = 0; i < theColors.Length; i++)
                    yield return theColors[i];
            }

      是不是很奇怪,yield return是what鬼,并且如果yield return在第一次迭代中返回,循环就永远不会获得其后续迭代了

      莫慌,慢慢来,通过迭代器块了解yield。迭代器块是有一个或多个yield语句的代码块,它可以是方法主体、访问器主体或运算符主体中的任意一种。和普通代码块不同,它描述了希望编译器为我们创建枚举器类的行为,包含两个特殊语句

      1. yield  return  指定了序列中返回的下一项

      2. yield  break  指定在序列中没有其他项  

    下面是通过迭代器创建可枚举类型的例子

    using System;
    using System.Collections;
    using System.Collections.Generic;
    
    namespace 枚举器
    {
        class Program
        {
            static void Main(string[] args)
            {
                MyClass mc = new MyClass();
                foreach(string str in mc)
                {
                    Console.Write("{0} ",str);
                }
                Console.ReadKey();
            }
        }
    
        class MyClass
        {
            public IEnumerator<string> GetEnumerator()
            {
                IEnumerable<string> MyColor = Colors();
                return MyColor.GetEnumerator();
            }
    
            public IEnumerable<string> Colors()
            {
                string[] theColors = { "black", "white", "gray" };
                for (int i = 0; i < theColors.Length; i++)
                    yield return theColors[i];
            }
        }
    }

    结果为:  black  white  gray

    五、产生多个可枚举类型

    using System;
    using System.Collections;
    using System.Collections.Generic;
    
    namespace 枚举器
    {
        class Program
        {
            static void Main(string[] args)
            {
                MyClass mc = new MyClass();
                foreach(string str in mc.Colors())
                {
                    Console.WriteLine("{0}",str);
                }
    
                foreach(string str in mc.Colors2())
                {
                    Console.WriteLine("{0}", str);
                }
                Console.ReadKey();
            }
        }
    
        class MyClass
        {
            public IEnumerable<string> Colors()    //顺序输出
            {
                string[] theColors = { "black", "white", "gray" };
                for (int i = 0; i < theColors.Length; i++)
                    yield return theColors[i];
            }
    
            public IEnumerable<string> Colors2()    //逆序输出
            {
                string[] theColors = { "black", "white", "gray" };
                for (int i=theColors.Length - 1; i >=0; i--)
                    yield return theColors[i];
            }
        }
    }

    六、迭代器作为属性输出

      和前面的代码非常类似,将前面的color函数改了

    using System;
    using System.Collections;
    using System.Collections.Generic;
    
    namespace 枚举器
    {
        class Program
        {
            static void Main(string[] args)
            {
                MyClass mc = new MyClass();
                foreach (string str in mc.Colors1)
                {
                    Console.Write("{0}  ", str);
                }
                Console.WriteLine();
                foreach (string str in mc.Colors2)
                {
                    Console.Write("{0}  ", str);
                }
                Console.ReadKey();
            }
        }
    
        class MyClass
        {
            public IEnumerable<string> Colors1
            {
                get
                {
                    string[] theColors = { "black", "white", "gray" };
                    for (int i = 0; i < theColors.Length; i++)
                        yield return theColors[i];
                }
            }
    
            public IEnumerable<string> Colors2
            {
                get
                {
                    string[] theColors = { "black", "white", "gray" };
                    for (int i = theColors.Length - 1; i >= 0; i--)
                        yield return theColors[i];
                }
            }
        }
    }

          

  • 相关阅读:
    Django框架 之 logging配置
    Django框架 之 中间件
    Django框架 之 form组件
    Django框架 之 Ajax
    Django框架 之 Pagination分页实现
    3张图带你看懂扩展KMP(EXKMP)
    [TJOI2018]游园会(状压dp+LCS)
    [BZOJ 2959] 长跑(LCT+并查集)
    [NAIPC2016]Jewel Thief(决策单调性+分治)
    [BZOJ 1563] [NOI 2009] 诗人小G(决策单调性)
  • 原文地址:https://www.cnblogs.com/forever-Ys/p/10309683.html
Copyright © 2020-2023  润新知