zoukankan      html  css  js  c++  java
  • 枚举器和迭代器

    枚举器和可枚举类型

    枚举器是一个可以依次返回请求的数组中元素的类实例对象,“知道”项的次序并跟踪它在序列中的位置,然后返回请求的当前项。

    对于有枚举器的类型而言,必须有一个方法来获取它。获取一个对象枚举器的方法是调用对象的GetEnumerator方法。实现GetEnumerator方法的类型叫做可枚举类型

        class Program
        {
            public interface IJohn
            {
                void MyMethod();
            }
    
            static void Main()
            {
                int[] test = { 1, 2, 3 };
                var a = test.GetEnumerator();
                Type t = a.GetType();
                Console.WriteLine($"{t.Name}");
                Console.WriteLine($"{typeof(IJohn).Name}");
                Console.WriteLine($"{typeof(IEnumerable).Name}");
            }
            
        }
    

    image-20210913225647351

    IEnumerator接口

    实现了IEnumerator接口的枚举器包含3个函数成员:Current,MoveNext以及Reset

    • Current是返回序列中当前位置项的属性
      • 它是只读属性
      • 返回object类型的引用,所以可以返回任何类型
    • MoveNext是把枚举器位置前进到集合中下一项的方法,返回布尔值,指示新的位置是有效位置还是已经超过了序列的尾部
      • 如果新的位置是有效的,方法返回true
      • 如果新的是位置是无效的,方法返回false
      • 枚举器的原始位置在序列中的第一项之前,是-1,因此MoveNext必须在第一次使用Current之前调用。
    • Reset是把位置重置为原始状态的方法
    static void Main()
            {
                int[] MyArray = { 10, 11, 12, 13 };
                IEnumerator ie = MyArray.GetEnumerator();
    
                while (ie.MoveNext())
                {
                    int i = (int)ie.Current;
                    Console.WriteLine($"{i}");
                }
            }
    

    image-20210913230639301

    IEnumerable接口

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

        class MyColors : IEnumerable
        {
            string[] Colors = { "Red", "Yellow", "Blue" };
            public IEnumerator GetEnumerator()
            {
                return new ColorEnumerator(Colors);
            }
        }
        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 - 1)
                {
                    _position++;
                    return true;
                }
                else
                {
                    return false;
                }
            }
            public void Reset()
            {
                _position = -1;
            }
        }
        class Program
        {
            public interface IJohn
            {
                void MyMethod();
            }
    
            static void Main()
            {
                var mc = new MyColors();
                foreach (var color in mc)
                {
                    Console.WriteLine(color);
                }
            }
            
        }
    

    image-20210913233257693

    泛型枚举接口

    实际上,大多数情况下应该使用泛型版本IEnumeratble<T>IEnumerator<T>,两者的区别如下:

    对于非泛型接口形式:

    • IEnumerable接口的GetEnumerator返回实现IEnumerator枚举器的实例
    • 实现IEnumerator的类实现了Current属性,它返回的是object,然后必须把它转化为实际类型的对象

    对于泛型接口形式:

    • IEnumerable<T>接口的GetEnumerator方法返回实现IEnumerator<T>的枚举器类的实例
    • 实现IEnumerator<T>的类实现了Current属性,返回的是实际类型的对象,而不是object基类的引用

    由此可见,非泛型接口的实现不是类型安全的,它们返回的是object类型的引用,然后必须转化为实际类型,而泛型接口的枚举器是类型安全的,它返回的实际类型的引用。

    迭代器

    迭代器实际是在类中的一种特殊的结构,它返回一个泛型枚举器或可枚举类型,yield return语句声明这是枚举中的下一项。

    迭代器块是由一个或多个yield语句的代码块,它可以是:方法主体;访问器主体;运算符主体;

    产生枚举器的迭代器:

    public  IEnumerator<string> IteratorMethod()
    {
        ...
            yield return ...;
    }
    

    产生可枚举类型的迭代器:

    public IEnumerable<string> IteratorMethod()
    {
        ....
            yield return ...;
    }
    

    使用迭代器来创建枚举器

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    { 
        class MyClass1
        {
            public IEnumerator<string> GetEnumerator()
            {
                return BlackAndWhite();
            }
            public IEnumerator<string> BlackAndWhite()
            {
                yield return "Black";
                yield return "Gray";
                yield return "White";
            }
        }
        
        class Program
        {
    
            static void Main()
            {
                var mc = new MyClass1();
                foreach (var s in mc)
                {
                    Console.WriteLine(s);
                }
                
            }
            
        }
    }
    
    

    image-20210914231518677

    上述代码,BlackAndWhite方法是一个迭代器块,可为Myclass1类产生并返回枚举器,MyClass1实现了GetEnumerator方法,这意味着它是可枚举类型,该方法调用了BlackAndWhite方法,并返回了BlackAndWhite返回的枚举器。

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

    使用迭代器来创建可枚举类型意思是迭代器块返回的是可枚举类型,并不是说迭代器块所在的类因为有了迭代器块就变成了可枚举类型,迭代器块所在的类是不是可枚举类型,是由其实现没实现GetEnumerator方法决定的,如果实现了,则就是可枚举类型,这种情况下,GetEnumerator方法可调用迭代器产生的可枚举类型的GetEnumerator方法来返回枚举器。

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    { 
        class MyClass1
        {
            public IEnumerator<string> GetEnumerator()
            {
                return BlackAndWhite().GetEnumerator();
            }
            public IEnumerable<string> BlackAndWhite()
            {
                yield return "Black";
                yield return "Gray";
                yield return "White";
            }
        }
        
        class Program
        {
    
            static void Main()
            {
                var mc = new MyClass1();
                foreach (var s in mc)
                {
                    Console.WriteLine(s);
                }
                
            }
            
        }
    }
    
    

    image-20210914232508023

    常见的迭代器模式

    • 在类中设置返回枚举器的迭代器时,就必须通过实现GetEnumerator让类可枚举,如果不实现该方法,则设置的枚举器就失去了意义,因为也不能通过调用该迭代器来实现遍历的目的(遍历的是可枚举类,而不是枚举器)
    • 如果在类中设置返回可枚举类型的迭代器,可以让类实现GetEnumerator让类枚举,或者不实现该方法,而让类不可枚举。
      • 如果实现了GetEnumerator,那么该方法必须调用迭代器返回的可枚举类型的GetEnumerator方法来返回枚举器。
      • 如果不实现GetEnumerator,仍然可以使用由迭代器返回的可枚举类,只需要调用迭代器方法,即可实现遍历。
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    { 
        class MyClass1
        {
            //public IEnumerator<string> GetEnumerator()
            //{
            //    return BlackAndWhite().GetEnumerator();
            //}
            public IEnumerable<string> BlackAndWhite()
            {
                yield return "Black";
                yield return "Gray";
                yield return "White";
            }
        }
        
        class Program
        {
    
            static void Main()
            {
                var mc = new MyClass1();
                foreach (var s in mc.BlackAndWhite())
                {
                    Console.WriteLine(s);
                }
                
            }
            
        }
    }
    
    

    image-20210914233458914

    下面错误实例阐述的是:需要遍历的是可枚举类型,而不是枚举器。

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    { 
        class MyClass1
        {
            //public IEnumerator<string> GetEnumerator()
            //{
            //    return BlackAndWhite().GetEnumerator();
            //}
            public IEnumerable<string> BlackAndWhite()
            {
                yield return "Black";
                yield return "Gray";
                yield return "White";
            }
        }
        
        class Program
        {
    
            static void Main()
            {
                var mc = new MyClass1();
                foreach (var s in mc.BlackAndWhite().GetEnumerator())
                {
                    Console.WriteLine(s);
                }
                
            }
            
        }
    }
    
    

    image-20210914233737705

    产生多个可枚举类型

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    { 
    class Spectrum
        {
            string[] colors = { "violet", "blue", "cyan", "green", "yellow", "orange", "red" };
            public IEnumerable<string> UVtoIR()
            {
                for (int i = 0; i < colors.Length; i++)
                {
                    yield return colors[i];
                }
            }
            public IEnumerable<string> IRtoUV()
            {
                for (int i = colors.Length - 1; i >= 0; i--)
                {
                    yield return colors[i];
                }
            }
        }
        
        class Program
        {
          
            public static void Main()
            {
                var a = new Spectrum();
    
                foreach (var color in a.UVtoIR())
                {
                    Console.Write($"{color}->");
                  
                }
                Console.WriteLine();
    
                foreach (var color in a.IRtoUV())
                {
                    Console.Write($"{color}->");
    
                }
                Console.WriteLine();
            }
    
    
                
            
            
        }
    }
    
    

    image-20210914234627171

    上述代码,Spectrum类本身并不是可枚举类型,但它定义了两个枚举类型迭代器,通过调用该迭代器,也可以完成不同的迭代遍历任务。

    将迭代器作为属性

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    { 
        class Spectrum
        {
            bool _listFromUVtoIR;
            string[] colors = { "violet", "blue", "cyan", "green", "yellow", "orange", "red" };
    
            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];
                    }
                }
            }
        }
        
        class Program
        {
    
            static void Main()
            {
                var startUV = new Spectrum(true);
                var startIR = new Spectrum(false);
    
                foreach(string color in startUV)
                {
                    Console.Write($"{color}->");
                }
                Console.WriteLine();
    
                foreach (string color in startIR)
                {
                    Console.Write($"{color}->");
                }
                Console.WriteLine();
                
            }
            
        }
    }
    
    

    image-20210914230850443

    有关迭代器其他事项

    • 迭代器需要System.Collections.Generic命名空间
    • 在编译生成的枚举器中,Reset方法并没有实现,所以调用该方法,会抛出System.NotSupportedException异常。
    • 在后台,由编译器生成的枚举器类是包含4个状态的状态机:

    image-20210914233146792

    ##### 愿你一寸一寸地攻城略地,一点一点地焕然一新 #####
  • 相关阅读:
    centos7 killall 命令
    移动硬盘拒绝访问问题解决方法
    Linux实现内容分发的主备模式的智能DNS
    UWB DWM1000 跟随小车原理--- 原理代码解析
    DWM1000 自动应答代码实现与实例
    UWB DWM1000 跟随小车原理---一张图演示
    DWM1000 帧过滤代码实现
    Bphero-UWB 基站0 和 电脑串口数据格式定义
    DW1000 用户手册中文版 附录3:双向测距(Two-Way Ranging)
    DW1000 用户手册中文版 附录2 IEEE-802.15.4 MAC层
  • 原文地址:https://www.cnblogs.com/johnyang/p/15265534.html
Copyright © 2011-2022 走看看