引言:数据库
在C# 1.0中咱们常用foreach来遍历一个集合中的元素,然而一个类型要可以使用foreach关键字来对其进行遍历必须实现IEnumerable或IEnumerable<T>接口,(之因此来必需要实现IEnumerable这个接口,是由于foreach是迭代语句,要使用foreach必需要有一个迭代器才行的,然而IEnumerable接口中就有IEnumerator GetEnumerator()方法是返回迭代器的,因此实现了IEnumerable接口,就必须实现GetEnumerator()这个方法来返回迭代器,有了迭代器就天然就可使用foreach语句了),然而在C# 1.0中要得到迭代器就必须实现IEnumerable接口中的GetEnumerator()方法,然而要实现一个迭代器就必须实现IEnumerator接口中的bool MoveNext()和void Reset()方法,然而 C# 2.0中提供 yield关键字来简化迭代器的实现,这样在C# 2.0中若是咱们要自定义一个迭代器就容易多了。下面就具体介绍了C# 2.0 中如何提供对迭代器的支持.函数
1、迭代器的介绍工具
迭代器你们能够想象成数据库的游标,即一个集合中的某个位置,C# 1.0中使用foreach语句实现了访问迭代器的内置支持,使用foreach使咱们遍历集合更加容易(比使用for语句更加方便,而且也更加容易理解),foreach被编译后会调用GetEnumerator来返回一个迭代器,也就是一个集合中的初始位置(foreach其实也至关因而一个语法糖,把复杂的生成代码工做交给编译器去执行)。测试
2、C#1.0如何实现迭代器this
在C# 1.0 中实现一个迭代器必须实现IEnumerator接口,下面代码演示了传统方式来实现一个自定义的迭代器:编码
1 using System; 2 using System.Collections; 3 4 namespace 迭代器Demo 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 Friends friendcollection = new Friends(); 11 foreach (Friend f in friendcollection) 12 { 13 Console.WriteLine(f.Name); 14 } 15 16 Console.Read(); 17 } 18 } 19 20 /// <summary> 21 /// 朋友类 22 /// </summary> 23 public class Friend 24 { 25 private string name; 26 public string Name 27 { 28 get { return name; } 29 set { name = value; } 30 } 31 public Friend(string name) 32 { 33 this.name = name; 34 } 35 } 36 37 /// <summary> 38 /// 朋友集合 39 /// </summary> 40 public class Friends : IEnumerable 41 { 42 private Friend[] friendarray; 43 44 public Friends() 45 { 46 friendarray = new Friend[] 47 { 48 new Friend("张三"), 49 new Friend("李四"), 50 new Friend("王五") 51 }; 52 } 53 54 // 索引器 55 public Friend this[int index] 56 { 57 get { return friendarray[index]; } 58 } 59 60 public int Count 61 { 62 get { return friendarray.Length; } 63 } 64 65 // 实现IEnumerable<T>接口方法 66 public IEnumerator GetEnumerator() 67 { 68 return new FriendIterator(this); 69 } 70 } 71 72 /// <summary> 73 /// 自定义迭代器,必须实现 IEnumerator接口 74 /// </summary> 75 public class FriendIterator : IEnumerator 76 { 77 private readonly Friends friends; 78 private int index; 79 private Friend current; 80 internal FriendIterator(Friends friendcollection) 81 { 82 this.friends = friendcollection; 83 index = 0; 84 } 85 86 #region 实现IEnumerator接口中的方法 87 public object Current 88 { 89 get 90 { 91 return this.current; 92 } 93 } 94 95 public bool MoveNext() 96 { 97 if (index + 1 > friends.Count) 98 { 99 return false; 100 } 101 else 102 { 103 this.current = friends[index]; 104 index++; 105 return true; 106 } 107 } 108 109 public void Reset() 110 { 111 index = 0; 112 } 113 114 #endregion 115 } 116 }
运行结果(上面代码中都有详细的注释,这里就不说明了,直接上结果截图):spa
3、使用C#2.0的新特性简化迭代器的实现code
在C# 1.0 中要实现一个迭代器必须实现IEnumerator接口,这样就必须实现IEnumerator接口中的MoveNext、Reset方法和Current属性,从上面代码中看出,为了实现FriendIterator迭代器须要写40行代码,然而在C# 2.0 中经过yield return语句简化了迭代器的实现,下面看看C# 2.0中简化迭代器的代码:对象
1 namespace 简化迭代器的实现 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 Friends friendcollection = new Friends(); 8 foreach (Friend f in friendcollection) 9 { 10 Console.WriteLine(f.Name); 11 } 12 13 Console.Read(); 14 } 15 } 16 17 /// <summary> 18 /// 朋友类 19 /// </summary> 20 public class Friend 21 { 22 private string name; 23 public string Name 24 { 25 get { return name; } 26 set { name = value; } 27 } 28 public Friend(string name) 29 { 30 this.name = name; 31 } 32 } 33 34 /// <summary> 35 /// 朋友集合 36 /// </summary> 37 public class Friends : IEnumerable 38 { 39 private Friend[] friendarray; 40 41 public Friends() 42 { 43 friendarray = new Friend[] 44 { 45 new Friend("张三"), 46 new Friend("李四"), 47 new Friend("王五") 48 }; 49 } 50 51 // 索引器 52 public Friend this[int index] 53 { 54 get { return friendarray[index]; } 55 } 56 57 public int Count 58 { 59 get { return friendarray.Length; } 60 } 61 62 // C# 2.0中简化迭代器的实现 63 public IEnumerator GetEnumerator() 64 { 65 for (int index = 0; index < friendarray.Length; index++) 66 { 67 // 这样就不须要额外定义一个FriendIterator迭代器来实现IEnumerator 68 // 在C# 2.0中只须要使用下面语句就能够实现一个迭代器 69 yield return friendarray[index]; 70 } 71 } 72 } 73 }
在上面代码中有一个yield return 语句,这个语句的做用就是告诉编译器GetEnumerator方法不是一个普通的方法,而是实现一个迭代器的方法,当编译器看到yield return语句时,编译器知道须要实现一个迭代器,因此编译器生成中间代码时为咱们生成了一个IEnumerator接口的对象,你们能够经过Reflector工具进行查看,下面是经过Reflector工具获得一张截图:blog
从上面截图能够看出,yield return 语句实际上是C#中提供的另外一个语法糖,简化咱们实现迭代器的源代码,把具体实现复杂迭代器的过程交给编译器帮咱们去完成,看来C#编译器真是作得很是人性化,把复杂的工做留给本身作,让咱们作一个简单的工做就行了。
4、迭代器的执行过程
为了让你们更好的理解迭代器,下面列出迭代器的执行流程:
5、迭代器的延迟计算
从第四部分中迭代器的执行过程当中能够知道迭代器是延迟计算的, 由于迭代的主体在MoveNext()中实现(由于在MoveNext()方法中访问了集合中的当前位置的元素),Foreach中每次遍历执行到in的时候才会调用MoveNext()方法,因此迭代器能够延迟计算,下面经过一个示例来演示迭代器的延迟计算:
namespace 迭代器延迟计算Demo { class Program { /// <summary> /// 演示迭代器延迟计算 /// </summary> /// <param name="args"></param> static void Main(string[] args) { // 测试一 //WithIterator(); //Console.Read(); // 测试二 //WithNoIterator(); //Console.Read(); // 测试三 foreach (int j in WithIterator()) { Console.WriteLine("在main输出语句中,当前i的值为:{0}", j); } Console.Read(); } public static IEnumerable<int> WithIterator() { for (int i = 0; i < 5; i++) { Console.WriteLine("在WithIterator方法中的, 当前i的值为:{0}", i); if (i > 1) { yield return i; } } } public static IEnumerable<int> WithNoIterator() { List<int> list = new List<int>(); for (int i = 0; i < 5; i++) { Console.WriteLine("当前i的值为:{0}", i); if (i > 1) { list.Add(i); } } return list; } } }
当运行测试一的代码时,控制台中什么都不输出,缘由是生成的迭代器延迟了i 值的输出,你们能够用Reflector工具反编译出编译器生成的中间语言代码就能够发现缘由了,下面是一张截图:
从图中能够看出,WithIterator()被编译成下面的代码了(此时编译器把咱们本身方法体写的代码给改了):
public static IEnumerable<int> WithIterator() { return new <WithIterator>d_0(-2); }
从而当咱们测试一的代码中调用WithIterator()时,对于编译器而言,就是实例化了一个<WithIterator>d_0的对象(<WithIterator>d_0类是编译看到WithIterator方法中包含Yield return 语句生成的一个迭代器类),因此运行测试一的代码时,控制台中什么都不输出。
当运行测试二的代码时,运行结果就如咱们指望的那样输出(这里的运行结果就不解释了,列出来是为了更好说明迭代器的延迟计算):
当咱们运行测试三的代码时,运行结果就有点让咱们感到疑惑了, 下面先给出运行结果截图,而后在分析缘由。
可能刚开始看到上面的结果不少人会有疑问,为何2,3,4会运行两次的呢?下面具体为你们分析下为何会有这样的结果。
测试代码三中经过foreach语句来遍历集合时,当运行in的时候就会运行IEnumerator.MoveNext()方法,下面是上面代码的MoveNext()方法的代码截图:
从截图中能够看到有Console.WriteLine()语句,因此用foreach遍历的时候才会有结果输出(主要是由于foreach中in 语句调用了MoveNext()方法),至于为何2,3,4会运行两行,主要是由于这里有两个输出语句,一个是WithIterator方法体内for语句中的输出语句,令一个是Main函数中对WithIterator方法返回的集合进行迭代的输出语句,在代码中都有明确指出,相信你们通过这样的解释后就不难理解测试三的运行结果了。
6、小结
本专题主要介绍了C# 2.0中经过yield return语句对迭代器实现的简化,然而对于编译器而言,却没有简化,它一样生成了一个类去实现IEnumerator接口,只是咱们开发人员去实现一个迭代器获得了简化而已。但愿经过本专题,你们能够对迭代器有一个进一步的认识,而且迭代器的延迟计算也是Linq的基础,本专题以后将会和你们介绍C# 3.0中提出的新特性,然而C# 3.0中提出来的Lambda,Linq能够说是完全改变咱们编码的风格,后面的专题中将会和你们一一分享我所理解C# 3.0 中的特性。