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

    ##### 愿你一寸一寸地攻城略地,一点一点地焕然一新 #####
  • 相关阅读:
    区别@ControllerAdvice 和@RestControllerAdvice
    Cannot determine embedded database driver class for database type NONE
    使用HttpClient 发送 GET、POST、PUT、Delete请求及文件上传
    Markdown语法笔记
    Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required
    Mysql 查看连接数,状态 最大并发数(赞)
    OncePerRequestFilter的作用
    java连接MySql数据库 zeroDateTimeBehavior
    Intellij IDEA 安装lombok及使用详解
    ps -ef |grep xxx 输出的具体含义
  • 原文地址:https://www.cnblogs.com/johnyang/p/15265534.html
Copyright © 2011-2022 走看看