IEnumerable、IEnumerator接口封装了迭代器功能,有了它,我们不需要将内部集合暴露出去,外界只需要访问我的迭代器接口方法即可遍历数据。
在C#中,使用foreach语句来遍历集合。foreach语句是微软提供的语法糖,使用它可以简化C#内置迭代器的使用复杂性。编译foreach语句,会生成调用GetEnumerator和MoveNext方法以及Current属性的代码。
有两种实现迭代器的方式,一是自己继承IEnumerable接口,比较复杂;二是使用yield,简化操作,下面会有案例。
反编译foreach,生成类似下面这段代码:
1 IEnumerator<Student> studentEnumerator = studentList.GetEnumerator(); 2 while (studentEnumerator.MoveNext()) 3 { 4 var currentStudent = studentEnumerator.Current as Student; 5 Console.WriteLine("Id = {0}, Name = {1}, Age = {2}", currentStudent.Id, currentStudent.Name, currentStudent.Age); 6 }
案例1:给自己的类增加迭代器功能
1 public class StudentSet : IEnumerable 2 { 3 private Student[] students; 4 public StudentSet(Student[] inputStudents) 5 { 6 students = new Student[inputStudents.Length]; 7 for (int i = 0; i < inputStudents.Length; i++) 8 { 9 students[i] = inputStudents[i]; 10 } 11 } 12 13 IEnumerator IEnumerable.GetEnumerator() 14 { 15 return (IEnumerator)GetEnumerator(); 16 } 17 18 public StudentEnumerator GetEnumerator() 19 { 20 return new StudentEnumerator(students); 21 } 22 } 23 24 public class StudentEnumerator : IEnumerator 25 { 26 public Student[] students; 27 int position = -1; 28 public StudentEnumerator(Student[] students) 29 { 30 this.students = students; 31 } 32 33 public bool MoveNext() 34 { 35 position++; 36 return (position < students.Length); 37 } 38 39 public void Reset() 40 { 41 position = -1; 42 } 43 44 object IEnumerator.Current 45 { 46 get 47 { 48 return Current; 49 } 50 } 51 52 public Student Current 53 { 54 get 55 { 56 try 57 { 58 return students[position]; 59 } 60 catch (IndexOutOfRangeException) 61 { 62 throw new InvalidOperationException(); 63 } 64 } 65 } 66 }
案例2:使用yield实现迭代器功能
public class MyCollection { private int[] list = new int[] { 1, 2, 3, 4 }; public IEnumerator<int> GetEnumerator() { for (int i = 0; i < list.Length; i++) { yield return list[i]; } } }
MyCollection col = new MyCollection(); foreach (var item in col) { Console.WriteLine(item); }