zoukankan      html  css  js  c++  java
  • IEnumerable与IEnumerator

    IEnumerable接口

    IEnumerable接口:实现该接口的类,表明该类下有可以枚举的元素

    public interface IEnumerable
    {
          //返回一个实现了IEnumerator接口的对象
          //我的理解:实现该接口的类,用其下哪个可枚举的元素实现该方法那么当该类被枚举时,将得到该类下的某个可枚举元素的元素
          IEnumerator GetEnumerator();
    }
    public class TestEnumerable : IEnumerable
      {
        //可枚举元素
        private Student[] _students;
       
        public TestEnumerable(Student[] list)
        {
            _students = list;
        }
       
        public IEnumerator GetEnumerator()
        {
            //当TestEnumerator被枚举时,将得到_students的元素
            return _students.GetEnumerator();
        }
    }
       
    public class Student
    {
        public string ID { get; set; }
       
        public string Name { get; set; }
       
        public Student(string id, string name)
        {
            ID = id;
            Name = name;
        }
    }

    调用代码:

    public static void Main()
    {
        Student[] students = new Student[3]
        {
            new Student("ID1", "Name1"),
            new Student("ID2", "Name2"),
            new Student("ID3", "Name3"),
        };
      
        TestEnumerable testEnumerable = new TestEnumerable(students);
      
        foreach (Student s in testEnumerable)
        {
            Console.WriteLine("ID:{0};Name:{1}", s.ID, s.Name);
        }
    }

    如上文所说, TestEnumerable类用foreach枚举,得到的每一个对象就是上文提到的“可以枚举的元素(Student[])的每一个对象” 。

     IEnumerator接口:枚举的真正实现

    public interface IEnumerator
    {
        //获取集合中的当前元素
        object Current{get;set;}
      
        //枚举数推进到集合的下一个元素,当存在下一个对象时返回true,不存在时返回false
        bool MoveNext();
      
        //枚举数设置为其初始位置,该位置位于集合中第一个元素之前
        void Reset();
    }
    public class TestEnumerator : IEnumerator
    {
      //指针,指向枚举元素 (为什么这里是-1,因为在MoveNext()中,_position++)
      private int _position = -1;
      private Student[] _students;
      public TestEnumerator() { }
      public TestEnumerator(Student[] list)
      {
        _students = list;
      }
    
      /// <summary>
      /// 获取当前元素
      /// </summary>
      public object Current
      {
        get
        {
          return _students[_position];
        }
      }
    
      /// <summary>
      /// 移动到下一个元素
      /// </summary>
      /// <returns>成功移动到下一个元素返回:true;失败返回:false</returns>
      public bool MoveNext()
      {
        _position++;
        return _position < _students.Length;
      }
    
      /// <summary>
      /// 重置到第一个元素
      /// </summary>
      public void Reset()
      {
        _position = -1;
      }
    }
    
    public class TestEnumerable2 : IEnumerable
    {
      private Student[] _students;
      public TestEnumerable2(Student[] list)
      {
        _students = list;
      }
    
      public IEnumerator GetEnumerator()
      {
        return new TestEnumerator(_students);
      }
    }

    调用代码:

    public static void Main()
    {
        Student[] students = new Student[3]
        {
            new Student("ID1", "Name1"),
            new Student("ID2", "Name2"),
            new Student("ID3", "Name3"),
        };
      
        TestEnumerable2 testEnumerator = new TestEnumerable2(students);
      
        foreach (Student s in testEnumerator)
        {
            Console.WriteLine("ID:{0};Name:{1}", s.ID, s.Name);
        }
    }

    IEnumerable接口与IEnumerator接口算介绍完毕了。

    大家看IEnumerator接口的实现,就是实现一个属性,两个调整枚举点的方法。如果能有一种方式,能自动帮我们完成IEnumerator接口的实现,那么将方便很多。

    在.NET Framework 3.5中有了yield,这个关键字可以帮我们自动生成实现IEnumerator的类。

    yield

    yield关键字,可以帮我们自动生成一个实现了IEnumerator的类,来完成枚举。

    下面,我们先来看一下MSDN上的介绍

    1.foreach 循环的每次迭代都会调用迭代器方法
    2.迭代器方法运行到 yield return 语句时,会返回一个expression表达式并保留当前在代码中的位置
    3.当下次调用迭代器函数时执行从该位置重新启动

    其实msdn这里解释的有点啰嗦了。简单点说,就是迭代器运行到yield return语句时,将自动做两件事。

    1.记录当前访问的位置
    2.当下次调用迭代器函数时执行从该位置重新启动:MoveNext()

     下面我们来看一段实例代码:

    public class TestEnumerable3 : IEnumerable
    {
        private Student[] _students;
      
        public TestEnumerable3(Student[] list)
        {
            _students = list;
        }
          
        //实现与之前有不同
        public IEnumerator GetEnumerator()
        {
            //迭代集合
            foreach (Student stu in _students)
            {
                //返回当前元素,剩下的就交给yield了,它会帮我们生成一个实现了接口IEnumerator的类
                //来帮我们记住当前访问到哪个元素。
                yield return stu;
            }
        }
    }

    下面我贴出一段GetEnumerator()方法的IL代码,来证明yield return 帮我们自动生成了一个实现了IEnumerator接口的类

    .method public final hidebysig newslot virtual
        instance class [mscorlib]System.Collections.IEnumerator GetEnumerator () cil managed
    {
        // Method begins at RVA 0x228c
        // Code size 20 (0x14)
        .maxstack 2
        .locals init (
            [0] class TestBlog.TestEnumerable3/'<GetEnumerator>d__0',
                      
             //这句最重要,初始化了一个实现System.Collections.IEnumerator的class,存储在索引1的位置
            [1] class [mscorlib]System.Collections.IEnumerator
        )
      
        IL_0000: ldc.i4.0
        IL_0001: newobj instance void TestBlog.TestEnumerable3/'<GetEnumerator>d__0'::.ctor(int32)
        IL_0006: stloc.0
        IL_0007: ldloc.0
        IL_0008: ldarg.0
        IL_0009: stfld class TestBlog.TestEnumerable3 TestBlog.TestEnumerable3/'<GetEnumerator>d__0'::'<>4__this'
        IL_000e: ldloc.0
        IL_000f: stloc.1
        IL_0010: br.s IL_0012
      
        IL_0012: ldloc.1
        IL_0013: ret
    } // end of method TestEnumerable3::GetEnumerator

    最后就是调用代码:

    static void Main(string[] args)
    {
        Student[] students = new Student[3]
        {
            new Student("ID1", "Name1"),
            new Student("ID2", "Name2"),
            new Student("ID3", "Name3"),
        };
      
        TestEnumerable3 testEnumerable3 = new TestEnumerable3(students);
      
        foreach (Student s in testEnumerable3)
        {
            Console.WriteLine("ID:{0};Name:{1}", s.ID, s.Name);
        }
    }

    IEnumerable构建迭代方法

    /// <summary>
    /// IEnumerable构建迭代方法
    /// <param name="_students">排序集合</param>
    /// <param name="direction">排序顺序(true:顺序;false:倒序)</param>
    public IEnumerable SortStudents(Student[] _students,bool direction)
    {
        if (direction)
        {
            foreach (Student stu in _students)
            {
                yield return stu;
            }
        }
        else
        {
            for (int i = _students.Length; i != 0; i--)
            {
                yield return _students[i - 1];
            }
        }
    }

    细心的朋友一定发现了。这个方法的返回类型为IEnumerable,而不像实现接口IEnumerable的GetEnumerator()方法返回类型是IEnumerator。这就是IEnumerable构建迭代方法需要注意的地方。

    下面是测试代码:

    static void Main(string[] args)
    {
        Student[] students = new Student[3]
        {
            new Student("ID1", "Name1"),
            new Student("ID2", "Name2"),
            new Student("ID3", "Name3"),
        };

       foreach (Student s in SortStudents(students,false)) { Console.WriteLine("ID:{0};Name:{1}", s.ID, s.Name); } }

    感谢大家的耐心阅读。

  • 相关阅读:
    获取Oracle、SqlServer、Access中表名、字段和主键(转)
    Oracle事务控制总结
    Oracle数据类型
    Oracle中的数据字典技术及常用数据字典总结
    asp.net中的<%%>形式的详细用法总结
    一道sql面试题的解答
    求ax(2)+bx+c=0的解
    求发票的下一个号码
    软件设计师2008年12月下午试题4(C语言 动态规划)
    软件设计师1991下午试题1(流程图解析)
  • 原文地址:https://www.cnblogs.com/color-wolf/p/3727278.html
Copyright © 2011-2022 走看看