C#中foreach语句深入研究

 更新时间:2015年06月17日 10:15:47   投稿:junjie  
这篇文章主要介绍了C#中foreach语句深入研究,本文通过手动实现迭代器来了解foreach语句的本质,揭示其原理,需要的朋友可以参考下

GPT4.0+Midjourney绘画+国内大模型 会员永久免费使用!
如果你想靠AI翻身,你先需要一个靠谱的工具!

1、概述

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

2、使用foreach语句遍历集合

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

例1:使用foreach来遍历集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//************************************************************ 
// 
// foreach应用示例代码 
// 
// Author:三五月儿 
//  
// Date:2014/09/10 
// 
// 
//************************************************************ 
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代码:

1
2
3
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#内置迭代器提供的方法和属性来遍历集合,本例中还可以使用下面的代码来完成遍历操作:

1
2
3
4
5
6
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:手动实现迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
//************************************************************ 
// 
// foreach应用示例代码 
// 
// Author:三五月儿 
//  
// Date:2014/09/10 
// 
// 
//************************************************************ 
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语句后生成的代码与使用迭代器的代码完全一致。

蓄力AI

微信公众号搜索 “ 脚本之家 ” ,选择关注

程序猿的那些事、送书等活动等着你

相关文章

  • C#类和结构详解

    C#类和结构详解

    本文详细讲解了C#中的类和结构,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04
  • Unity UGUI LayoutRebuilder自动重建布局介绍及使用

    Unity UGUI LayoutRebuilder自动重建布局介绍及使用

    这篇文章主要为大家介绍了Unity UGUI LayoutRebuilder自动重建布局介绍及使用,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-07-07
  • Unity实现场景加载功能

    Unity实现场景加载功能

    这篇文章主要为大家详细介绍了Unity实现场景加载,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-10-10
  • c#.net多线程编程教学——线程同步

    c#.net多线程编程教学——线程同步

    c#.net多线程编程教学——线程同步...
    2007-04-04
  • C#判断上传文件是否是图片以防止木马上传的方法

    C#判断上传文件是否是图片以防止木马上传的方法

    这篇文章主要介绍了C#判断上传文件是否是图片以防止木马上传的方法,对于网络安全来说至关重要,需要的朋友可以参考下
    2014-07-07
  • 深入浅出23种设计模式

    深入浅出23种设计模式

    本文给大家详解23种设计模式,理解设计模式有助于在程序开发过程中灵活应用,需要的朋友可以参考下
    2015-08-08
  • c#解压文件的实例方法

    c#解压文件的实例方法

    该方法适应应用桌面快捷键压缩的文件,zip,rar格式的文件进行解压!
    2013-05-05
  • C#打印PDF文档的10种方法(小结)

    C#打印PDF文档的10种方法(小结)

    这篇文章主要介绍了C#打印PDF文档的10种方法(小结),可分多种情况来进行,如设置静默打印、指定打印页码范围和打印纸张大小、双面打印、黑白打印等等,一共分成10种等,感兴趣的可以了解一下
    2019-04-04
  • C#实现两个exe程序之间通信详解

    C#实现两个exe程序之间通信详解

    这篇文章主要为大家详细介绍了C#如何使用SendMessage实现两个程序之间的通信功能,文中的示例代码简洁易懂,需要的小伙伴可以参考下
    2023-07-07
  • 开源一个c# 新的雪花算法

    开源一个c# 新的雪花算法

    这篇文章主要介绍了开源一个c# 新的雪花算法的相关资料,帮助大家更好的理解和学习使用c#,感兴趣的朋友可以了解下
    2021-03-03

最新评论