C#中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语句后生成的代码与使用迭代器的代码完全一致。
微信公众号搜索 “ 脚本之家 ” ,选择关注
程序猿的那些事、送书等活动等着你
相关文章
Unity UGUI LayoutRebuilder自动重建布局介绍及使用
这篇文章主要为大家介绍了Unity UGUI LayoutRebuilder自动重建布局介绍及使用,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪2023-07-07
最新评论