foreach语句揭秘

1概述

本文通过手动实现迭代器来了解foreach语句的本质。

2 使用foreach语句遍历集合

在C#中,使用foreach语句来遍历集合。foreach语句是微软提供的语法糖,使用它可以简化C#内置迭代器的使用复杂性。编译foreach语句,会生成调用GetEnumerator和MoveNext方法以及Current属性的代码,这些方法和属性恰是C#内置迭代器所提供的。下面将通过实例来说明这一切。

例1 使用foreach来遍历集合

//************************************************************
//
// foreach应用示例代码
//
// Author:三五月儿
//
// Date:2014/09/10
//
// http://blog.csdn.net/yl2isoft
//
//************************************************************
using System;
using System.Collections;
using System.Collections.Generic;
namespace IEnumerableExp
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Student> studentList = new List<Student>()
            {
                new Student(){Id = 1, Name = "三五月儿", Age = 23},
                new Student(){Id = 2, Name = "张三丰", Age = 108},
                new Student(){Id = 3, Name = "艾尔克森", Age = 25},
                new Student(){Id = 3, Name = "穆里奇", Age = 27}
            };
            foreach (var student in studentList)
            {
                Console.WriteLine("Id = {0}, Name = {1}, Age = {2}", student.Id,student.Name,student.Age);
            }
        }
    }
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }
}

代码中,使用foreach语句遍历Student对象的集合,依次输出Student对象的Id,Name,Age属性值。使用ILDASM查看程序对应的IL代码,下面这些是与foreach语句相关的IL代码:

IL_00c6:  callvirt   instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<class IEnumerableExp.Student>::GetEnumerator()
IL_00d1:  call   instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<class IEnumerableExp.Student>::get_Current()
IL_0102:  call   instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<class IEnumerableExp.Student>::MoveNext()

在IL代码中,是不是找到了GetEnumerator和MoveNext方法以及Current属性的身影,可见:foreach语句确实是微软提供的用来支持C#内置迭代器操作的语法糖,因为这些方法和属性正是C#内置迭代器所提供的。

当然,除了使用foreach语句来遍历集合外,还可以使用C#内置迭代器提供的方法和属性来遍历集合,本例中还可以使用下面的代码来完成遍历操作:

IEnumerator<Student> studentEnumerator = studentList.GetEnumerator();
while (studentEnumerator.MoveNext())
{
     var currentStudent = studentEnumerator.Current as Student;
     Console.WriteLine("Id = {0}, Name = {1}, Age = {2}", currentStudent.Id, currentStudent.Name, currentStudent.Age);
}

在第二种方法中,通过调用GetEnumerator和MoveNext方法以及Current属性来完成遍历操作,是不是与foreach语句编译后生成的代码一致啊。

两种遍历方法,都会得到下图所示结果:

图1 遍历集合元素

查看代码中GetEnumerator和MoveNext方法以及Current属性的定义,发现GetEnumerator方法来自于IEnumerable接口,而MoveNext方法与Current属性来自于IEnumerator接口。实现C#迭代器都应该实现这两个接口。下面就手动实现一个迭代器来操作学生对象的集合。

3 手动实现一个迭代器

前面使用到的是C#内置迭代器,当然,我们完全可以手动实现一个自己的迭代器。

例2 手动实现迭代器

//************************************************************
//
// foreach应用示例代码
//
// Author:三五月儿
//
// Date:2014/09/10
//
// http://blog.csdn.net/yl2isoft
//
//************************************************************
using System;
using System.Collections;
using System.Collections.Generic;
namespace IEnumerableExp
{
    class Program
    {
        static void Main(string[] args)
        {
            Student[] students = new Student[4]
            {
                new Student(){Id = 1, Name = "三五月儿", Age = 23},
                new Student(){Id = 2, Name = "张三丰", Age = 108},
                new Student(){Id = 3, Name = "艾尔克森", Age = 25},
                new Student(){Id = 3, Name = "穆里奇", Age = 27}
            };
            StudentSet studentSet = new StudentSet(students);
            foreach (var student in studentSet)
            {
                Console.WriteLine("Id = {0}, Name = {1}, Age = {2}", student.Id, student.Name, student.Age);
            }
        }
    }

    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }

    public class StudentSet : IEnumerable
    {
        private Student[] students;
        public StudentSet(Student[] inputStudents)
        {
            students = new Student[inputStudents.Length];
            for (int i = 0; i < inputStudents.Length; i++)
            {
                students[i] = inputStudents[i];
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return (IEnumerator)GetEnumerator();
        }

        public StudentEnumerator GetEnumerator()
        {
            return new StudentEnumerator(students);
        }
    }

    public class StudentEnumerator : IEnumerator
    {
        public Student[] students;
        int position = -1;
        public StudentEnumerator(Student[] students)
        {
            this.students = students;
        }

        public bool MoveNext()
        {
            position++;
            return (position < students.Length);
        }

        public void Reset()
        {
            position = -1;
        }

        object IEnumerator.Current
        {
            get
            {
                return Current;
            }
        }

        public Student Current
        {
            get
            {
                try
                {
                    return students[position];
                }
                catch (IndexOutOfRangeException)
                {
                    throw new InvalidOperationException();
                }
            }
        }
    }
}

代码中定义学生集合类StudentSet,在类中使用Student类型的数组来保存学生元素,该类实现IEnumerable接口,所以StudentSet类必须实现IEnumerable接口的GetEnumerator方法,该方法返回实现了IEnumerator接口的迭代器StudentEnumerator。

下面来看看StudentEnumerator类的定义,StudentEnumerator表示遍历学生集合的迭代器,使用它提供的方法和属性可以遍历集合的元素,该类实现IEnumerator接口,所以必须实现IEnumerator接口提供的MoveNext和Reset方法以及Current属性。StudentEnumerator类使用Student类型的集合students来保存需要遍历的集合。使用私有变量position来记录元素的位置,一开始position被赋值为-1,定位于集合中第一个元素的前面,在Reset方法中也可以将position的值置为-1,表示回到遍历操作前的状态。在MoveNext方法中先将position加1,再将其与集合的长度进行比较,看是否已经遍历完了所有元素,若未完返回true,否则返回false。在只读属性Current的实现中通过代码students[position]返回students集合中position位置的元素值。在使用迭代器时,需要先调用MoveNext方法判断下一个元素是否存在,如存在使用Current属性得到这个值,若不存在则表示已经遍历完所有元素,将停止遍历操作。

代码中同样使用foreach语句来遍历StudentSet对象中的元素并输出,与使用内置迭代器的效果一致。

4 总结

实现迭代器需要借助于IEnumerable与IEnumerator接口,接口IEnumerator提供的方法GetEnumerator可以返回实现IEnumerator接口的迭代器,而IEnumerator接口中包含了实现迭代器所需的方法及属性的定义。凡是实现了迭代器的类都可以使用foreach语句来遍历其元素,因为foreach语句是微软提供的支持内置迭代器的语法糖,编译foreach语句后生成的代码与使用迭代器的代码完全一致。

时间: 2024-10-10 09:36:21

foreach语句揭秘的相关文章

foreach语句

foreach语句他无非就是for循环的封装,为了提高开发速度才创造出来的.他其实跟for循环一样,只不过写起来比较简便,他是1.5版本才出来的一种封装语法.并没有什么奇特之处他里面的机制就是for循环.如图7.3所示: 图7.3 首先放在冒号后面的,他可以是数组的类型也可以是集合类型还可以是枚举类型,也就是说只要写上类似于这种容器,能够容乃多个的就是行. 那这个i是什么意思呢?每次循环的时候,i他取出一个值交给变量j,变量j就是接收者,接收从数组或者集合里拿出来的值交给接收者,每次循环重新接收

Java之Foreach语句

Foreach是for语句的特殊简化版本,但任何时候的foreach语句都可以改写成for语句.Foreach语句在遍历数组等方面为程序员提供了很大的方便 语法如下: For(元素变量 x:遍历对象obj){ 引用了x的java语句 } 实例: package Foreach1; public class Foreach1 { public static void main(String[] args) { int [] arr={1,3,5,7,9,11,13,15};    //声明一维数组

【Java学习笔记】foreach语句(高级for)

1 package p2; 2 3 import java.util.ArrayList; 4 import java.util.HashMap; 5 import java.util.List; 6 import java.util.Map; 7 8 public class ForEachDemo { 9 10 public static void main(String[] args) { 11 /* 12 * foreach语句: 13 * 格式: 14 * for(类型 变量 : Co

MyBatis的foreach语句详解

MyBatis的foreach语句详解 MyBatis的foreach语句详解 1人收藏此文章, 我要收藏 发表于3个月前 , 已有113次阅读 共0个评论 foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合.foreach元素的属性主要有 item,index,collection,open,separator,close.item表示集合中每一个元素进行迭代时的别名,index指 定一个名字,用于表示在迭代过程中,每次迭代到的位置,open表示该语句以什么开始,se

foreach 语句

foreach  语句很适合用来枚举   如数组.列表.集合之类的数据结构中的元素.  不必准确知道元素个数.如果基数据不包含任何元素,则foreach循环不执行 foreach(<元素>in<基础数据>) { <代码块> } \ 首先create  一个 int型 数组,然后通过foreach循环打印出所有的数.

2017-9-17C#笔记(方法,方法参数 ,foreach语句)

方法: 方法作为类中最常见的最有用的一个成员,算是完成特定任务,实现特定任务的重要的编程模式. "更少的代码,更多的复用" (有些教程中,将方法称为函数,函数和方法没有本质的区别,但是通常自己写的俄实现特定的功能的代码块,叫做方法;有系统简介或者直接生成的不完整的代码块叫做函数.) 方法定义的一般形式: Static      返回类型         方法名(形式参数列表) { 声明部分 执行部分 } 事例1:编写方法判断一个数是否是完全数:完全数的定义如下:它所有的真因子(即除了自

从foreach语句枚举元素看数组

在foreach语句中使用枚举,可以迭代数组或集合中的元素,且无须知道集合中的元素的个数.如图显示了调用foreach方法的客户端和集合之间的关系.数组或集合实现带GetEnumerator()方法的IEnumerable接口.GetEnumerator()方法返回一个实现lEnumerable接口的枚举,接着foreach语句就可以使用IEnumerable接口迭代集合了. GetEnumerator()方法用IEnumerable接口定义,foreach语句并不真的需要在集合类中实现这个接口

Java Foreach语句使用总结

foreach语句是java5的新特征之一,在遍历数组.集合方面,foreach为开发人员提供了极大的方便. foreach语句是for语句的特殊简化版本,但是foreach语句并不能完全取代for语句,然而,任何的foreach语句都可以改写为for语句版本. foreach并不是一个关键字,习惯上将这种特殊的for语句格式称之为"foreach"语句.从英文字面意思理解foreach也就是"for 每一个"的意思.实际上也就是这个意思. foreach的语句格式

Java中的可变参数以及foreach语句

Java中的可变参数的定义格式如下: 返回值类型  方法名称(类型 ... 参数名称){} foreach语句的格式如下: for ( 数据类型  变量名称 :数据名称){ ... } public class NewDemo01 { public static void main(String[] args) { // TODO Auto-generated method stub fun(); fun(1); fun(1,2,3,4); } public static void fun(in