都知道在c#2.0里有 迭代器,那么这个迭代器到底是个啥东东呢 他跟foreach又有啥关系
1 IList<string> arr = new List<string>(); 2 arr.Add("smith"); 3 arr.Add("sherry"); 4 arr.Add("steve"); 5 arr.Add("salt"); 6 arr.Add("stefan"); 7 IEnumerator<string> emutor = arr.GetEnumerator(); 8 while (emutor.MoveNext()) 9 Console.WriteLine(emutor.Current);
这就是迭代器 他是专门针对集合元素的 ilist<string> 就是一个集合元素
ienumerator<string> 就是他的迭代器。可以通过moveNext()来枚举出他的所有子元素。
呐 我们来把while子句那一段换成这两句
1 foreach (string item in arr) 2 Console.WriteLine(item);
用ildasm进行反编译可以看到他们的il代码是完全一样的
1 IL_0045: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<string>::GetEnumerator() 2 IL_004a: stloc.2 3 .try 4 { 5 IL_004b: br.s IL_005b 6 IL_004d: ldloc.2 7 IL_004e: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<string>::get_Current() 8 IL_0053: stloc.1 9 IL_0054: ldloc.1 10 IL_0055: call void [mscorlib]System.Console::WriteLine(string) 11 IL_005a: nop 12 IL_005b: ldloc.2 13 IL_005c: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() 14 IL_0061: stloc.3 15 IL_0062: ldloc.3 16 IL_0063: brtrue.s IL_004d 17 IL_0065: leave.s IL_0077 18 } // end .try 19 finally 20 { 21 IL_0067: ldloc.2 22 IL_0068: ldnull 23 IL_0069: ceq 24 IL_006b: stloc.3 25 IL_006c: ldloc.3 26 IL_006d: brtrue.s IL_0076 27 IL_006f: ldloc.2 28 IL_0070: callvirt instance void [mscorlib]System.IDisposable::Dispose() 29 IL_0075: nop 30 IL_0076: endfinally 31 } // end handler
foreach这个是原来就有的语法 就是让你的c#代码“更优雅”由编译器支持的 让你代替 for(int i,i<5,i++)这样的写法
诸如此类的例子在c#里还有很多
呐看下这段代码呢
1 string[] arr = new string[] { "smith", "sherry", "Steve", "salt", "stefan" }; 2 foreach (string item in arr) 3 Console.WriteLine(item);
注意啊 虽然都是foreach 但是此arr非彼arr 不存在迭代器的说法 ,这个是数组
通过ildasm反编译你就可以看到并没有类似::GetEnumerator()这样的代码存在
但是这种代码又不一样,因为显示使用了迭代器
1 string[] arr = new string[] { "smith", "sherry", "Steve", "salt", "stefan" }; 2 IEnumerator emutor = arr.GetEnumerator(); 3 while (emutor.MoveNext()) 4 Console.WriteLine(emutor.Current);
使用il反编译的代码:
1 IL_0033: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Array::GetEnumerator() 2 IL_0038: stloc.1 3 IL_0039: br.s IL_0047 4 IL_003b: ldloc.1 5 IL_003c: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current() 6 IL_0041: call void [mscorlib]System.Console::WriteLine(object) 7 IL_0046: nop 8 IL_0047: ldloc.1 9 IL_0048: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
那么首先你得设计一个集合类 这个跟索引属性有点类似
1 public class Student 2 { 3 public string name { get; set; } 4 public int score { get; set; } 5 public Student(string firstName, int lastName) 6 { 7 this.name = firstName; 8 this.score = lastName; 9 } 10 } 11 //实现ienumerable接口的可枚举类型 12 13 public class Students : IEnumerable 14 { 15 private Student[] arr; 16 public Students(Student[] peoples) 17 { 18 arr = new Student[peoples.Length]; 19 for (int i = 0; i < peoples.Length; i++) 20 { 21 arr[i] = peoples[i]; 22 } 23 } 24 public IEnumerator GetEnumerator() 25 { 26 return new StudentsEnum(arr); 27 } 28 }
然后你得实现他的枚举器
1 //实现ienumerator接口的枚举函数 2 public class StudentsEnum : IEnumerator 3 { 4 private Student[] arr; 5 //当前项的索引 6 //这里是指针放到第一个集合成员之前 7 //而非指向第一个集合成员 8 private int i = -1; 9 public StudentsEnum(Student[] peoples) 10 { 11 this.arr = peoples; 12 } 13 #region IEnumerator 成员 14 public object Current 15 { 16 get 17 { 18 try 19 { 20 return arr[i]; 21 } 22 catch (IndexOutOfRangeException) 23 { 24 throw new IndexOutOfRangeException(); 25 } 26 } 27 } 28 public bool MoveNext() 29 { 30 i++; 31 return i < arr.Length; 32 } 33 34 public void Reset() 35 { 36 i = -1; 37 } 38 #endregion 39 }
实现了students集合的迭代器 那么我们接下来就可以使用它了
1 Student[] peopleArray = new Student[3] { new Student("smith", 45), new Student("sherry", 56), new Student("salt", 67) }; 2 Students people = new Students(peopleArray); 3 foreach (Student p in people) 4 Console.WriteLine("name:{0} \t score:{1}", p.name, p.score);
其实主要代码也就是枚举器的实现
我们有种偷懒的做法 ,那就是使用yield关键字
1 public class City 2 { 3 #region IEnumerable 成员 4 string[] m_Cities = { "New York", "Paris", "London" }; 5 public IEnumerator GetEnumerator() 6 { 7 Console.WriteLine(@"开始迭代~\(≧▽≦)/~啦啦啦"); 8 for (int i = 0; i < m_Cities.Length; i++) 9 yield return m_Cities[i]; // 产生枚举元素 10 } 11 #endregion 12 }
还是一样的调用
1 City c = new City(); 2 foreach (string item in c) 3 Console.WriteLine(item);
这都啥啊 我看着都晕了student类也没了 city的子元素是啥啊, 这不是索引器吗 像这样:
1 public string this[int n] 2 { 3 get { return m_Cities[n]; } 4 set { m_Cities[n] = value; } 5 }
你看都没有继承IEnumerable 还是可以同样的使用
是的GetEnumerator() 本意就是“获取一个索引器”
yield意思是 迭代 量产
而yield 搭配for语句使用 本意就是 “迭代出每一个元素”并“生成一个索引器”返回
说到这里相信你们理解到他的精髓了 迭代器 枚举器 索引器 其实他们指的是同一个东西 他们都针对集合使用
好了收工