以前用集合的时候,用foreach进行循环,也没深究里面的实现机制。昨日在博客园上看一大牛的文章,讲述yield return的问题,才知道原来是这样的啊。下面这些概念都摘抄自书上。
1. 基础知识
IEnumerable接口允许使用foreach循环,很多的集合类都实现这个接口,在foreach循环中,并不一定只能用集合类,可以定制类。简述下foreach循环中,迭代一个collectionObject集合的过程如下:
1)调用collectionObject.GetEnumerator(),返回一个IEnumerator引用。这个方法可以通过IEnumerator接口的实现代码来获得,可选的。
2)调用所返回的IEnumerator接口的movenext方法。
3)如果movenext方法返回为true,就使用IEnumerator接口的current属性来回去对象的一个引用,用于foreach循环。
4)重复前面两步,知道movenexr方法返回false为止。直至循环结束。
使用迭代器的两个类型的场合是:
1)如果要迭代一个类,可使用方法GetEnumerator(),其返回类型是IEnumerator
2)如果要迭代一个类成员,例如一个方法,则使用IEnumerable。
2 实例
举例说明
1)迭代一个类成员
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 using System.Collections; 5 namespace Ch12Ex01 6 { 7 class Program 8 { 9 public static IEnumerable GetList() 10 { 11 Console.WriteLine("GetList Method"); 12 yield return "string1"; 13 yield return "string2"; 14 yield return "string3"; 15 } 16 static void Main(string[] args) 17 { 18 var str = GetList(); 19 Console.WriteLine("Main method"); 20 foreach (string temp in str) 21 { 22 Console.WriteLine(temp); 23 } 24 Console.ReadKey(); 25 26 } 27 } 28 }
注意这里,getlist方法在调用的时候并没有运行方法体的内容,而是在迭代的时候才运行。所以输出的结果是先输出main method,在输出getlist method。
2)迭代一个类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 using System.Collections; 5 using System.Collections.Generic; 6 namespace Ch12Ex01 7 { 8 class Primes 9 { 10 private int min; 11 private int max; 12 public Primes() : this(2, 100) { } 13 public Primes(int minT, int maxT) 14 { 15 if (minT < 2) min = 2; 16 else 17 min = minT; 18 max = maxT; 19 } 20 public IEnumerator GetEnumerator() 21 { 22 for (int i = min; i < max;i++ ) 23 { 24 if (i % 2 == 0 && i % 3 == 0) 25 { 26 yield return i; 27 } 28 } 29 } 30 } 31 } 32 33 static void Main(string[] args) 34 { 35 Primes primes = new Primes(1, 1000); 36 foreach (var a in primes) 37 { 38 Console.WriteLine(a); 39 } 40 Console.ReadKey(); 41 42 }
3 内部实现机制
1)GetList反编译。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 internal class Program 2 { 3 // Methods 4 public static IEnumerable GetList() 5 { 6 return new <GetList>d__0(-2); 7 } 8 9 private static void Main(string[] args) 10 { 11 IEnumerable list = GetList(); 12 Console.WriteLine("Main method"); 13 foreach (string str in list) 14 { 15 Console.WriteLine(str); 16 } 17 Console.ReadKey(); 18 } 19 20 // Nested Types 21 [CompilerGenerated] 22 private sealed class <GetList>d__0 : IEnumerable<object>, IEnumerable, IEnumerator<object>, IEnumerator, IDisposable 23 { 24 // Fields 25 private int <>1__state; 26 private object <>2__current; 27 private int <>l__initialThreadId; 28 29 // Methods 30 [DebuggerHidden] 31 public <GetList>d__0(int <>1__state) 32 { 33 this.<>1__state = <>1__state; 34 this.<>l__initialThreadId = Thread.CurrentThread.ManagedThreadId; 35 } 36 37 private bool MoveNext() 38 { 39 switch (this.<>1__state) 40 { 41 case 0: 42 this.<>1__state = -1; 43 Console.WriteLine("GetList Method"); 44 this.<>2__current = "string1"; 45 this.<>1__state = 1; 46 return true; 47 48 case 1: 49 this.<>1__state = -1; 50 this.<>2__current = "string2"; 51 this.<>1__state = 2; 52 return true; 53 54 case 2: 55 this.<>1__state = -1; 56 this.<>2__current = "string3"; 57 this.<>1__state = 3; 58 return true; 59 60 case 3: 61 this.<>1__state = -1; 62 break; 63 } 64 return false; 65 } 66 67 [DebuggerHidden] 68 IEnumerator<object> IEnumerable<object>.GetEnumerator() 69 { 70 if ((Thread.CurrentThread.ManagedThreadId == this.<>l__initialThreadId) && (this.<>1__state == -2)) 71 { 72 this.<>1__state = 0; 73 return this; 74 } 75 return new Program.<GetList>d__0(0); 76 } 77 78 [DebuggerHidden] 79 IEnumerator IEnumerable.GetEnumerator() 80 { 81 return this.System.Collections.Generic.IEnumerable<System.Object>.GetEnumerator(); 82 } 83 84 [DebuggerHidden] 85 void IEnumerator.Reset() 86 { 87 throw new NotSupportedException(); 88 } 89 90 void IDisposable.Dispose() 91 { 92 } 93 94 // Properties 95 object IEnumerator<object>.Current 96 { 97 [DebuggerHidden] 98 get 99 { 100 return this.<>2__current; 101 } 102 } 103 104 object IEnumerator.Current 105 { 106 [DebuggerHidden] 107 get 108 { 109 return this.<>2__current; 110 } 111 } 112 } 113 }
从上面看到编译器生成了一个密封类,类名为<GetList>d__0,在main函数中调用getlist方法,其实只是新建了一个<GetList>d__0对象,当迭代的时候,才执行具体的内容,这也就能解释为啥先输出main method,再输出getlist method。
2)将Prime类进行反编译,如下。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 internal class Primes 2 { 3 // Fields 4 private int max; 5 private int min; 6 7 // Methods 8 public Primes() : this(2, 100) 9 { 10 } 11 12 public Primes(int minT, int maxT) 13 { 14 if (minT < 2) 15 { 16 this.min = 2; 17 } 18 else 19 { 20 this.min = minT; 21 } 22 this.max = maxT; 23 } 24 25 public IEnumerator GetEnumerator() 26 { 27 <GetEnumerator>d__0 d__ = new <GetEnumerator>d__0(0); 28 d__.<>4__this = this; 29 return d__; 30 } 31 32 // Nested Types 33 [CompilerGenerated] 34 private sealed class <GetEnumerator>d__0 : IEnumerator<object>, IEnumerator, IDisposable 35 { 36 // Fields 37 private int <>1__state; 38 private object <>2__current; 39 public Primes <>4__this; 40 public int <i>5__1; 41 42 // Methods 43 [DebuggerHidden] 44 public <GetEnumerator>d__0(int <>1__state) 45 { 46 this.<>1__state = <>1__state; 47 } 48 49 private bool MoveNext() 50 { 51 switch (this.<>1__state) 52 { 53 case 0: 54 this.<>1__state = -1; 55 this.<i>5__1 = this.<>4__this.min; 56 while (this.<i>5__1 < this.<>4__this.max) 57 { 58 if (((this.<i>5__1 % 2) != 0) || ((this.<i>5__1 % 3) != 0)) 59 { 60 goto Label_0080; 61 } 62 this.<>2__current = this.<i>5__1; 63 this.<>1__state = 1; 64 return true; 65 Label_0078: 66 this.<>1__state = -1; 67 Label_0080: 68 this.<i>5__1++; 69 } 70 break; 71 72 case 1: 73 goto Label_0078; 74 } 75 return false; 76 } 77 78 [DebuggerHidden] 79 void IEnumerator.Reset() 80 { 81 throw new NotSupportedException(); 82 } 83 84 void IDisposable.Dispose() 85 { 86 } 87 88 // Properties 89 object IEnumerator<object>.Current 90 { 91 [DebuggerHidden] 92 get 93 { 94 return this.<>2__current; 95 } 96 } 97 98 object IEnumerator.Current 99 { 100 [DebuggerHidden] 101 get 102 { 103 return this.<>2__current; 104 } 105 } 106 } 107 }
结合上面基础知识部分,就能知道迭代器的工作原理。每次迭代,其实就是执行movenext方法。