迭代器基础知识

    以前用集合的时候,用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)迭代一个类成员

 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 }
View Code

注意这里,getlist方法在调用的时候并没有运行方法体的内容,而是在迭代的时候才运行。所以输出的结果是先输出main method,在输出getlist method。

2)迭代一个类

 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         }
View Code

3 内部实现机制

1)GetList反编译。

  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 }
View Code

从上面看到编译器生成了一个密封类,类名为<GetList>d__0,在main函数中调用getlist方法,其实只是新建了一个<GetList>d__0对象,当迭代的时候,才执行具体的内容,这也就能解释为啥先输出main method,再输出getlist method。

2)将Prime类进行反编译,如下。

  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 }
View Code

结合上面基础知识部分,就能知道迭代器的工作原理。每次迭代,其实就是执行movenext方法。

原文地址:https://www.cnblogs.com/icbcfang/p/4390122.html