IEnumerator和IEnumerable

从名字常来看,IEnumerator是枚举器的意思,IEnumerable是可枚举的意思。
了解了两个接口代表的含义后,接着看源码:
IEnumerator:

 1 public interface IEnumerator
 2     {
 3         // Interfaces are not serializable
 4         // Advances the enumerator to the next element of the enumeration and
 5         // returns a boolean indicating whether an element is available. Upon
 6         // creation, an enumerator is conceptually positioned before the first
 7         // element of the enumeration, and the first call to MoveNext 
 8         // brings the first element of the enumeration into view.
 9         // 
10         bool MoveNext();
11     
12         // Returns the current element of the enumeration. The returned value is
13         // undefined before the first call to MoveNext and following a
14         // call to MoveNext that returned false. Multiple calls to
15         // GetCurrent with no intervening calls to MoveNext 
16         // will return the same object.
17         // 
18         Object Current {
19             get; 
20         }
21     
22         // Resets the enumerator to the beginning of the enumeration, starting over.
23         // The preferred behavior for Reset is to return the exact same enumeration.
24         // This means if you modify the underlying collection then call Reset, your
25         // IEnumerator will be invalid, just as it would have been if you had called
26         // MoveNext or Current.
27         //
28         void Reset();
29     }
View Code

IEnumerable:

1  public interface IEnumerable
2     {
3         // Interfaces are not serializable
4         // Returns an IEnumerator for this enumerable Object.  The enumerator provides
5         // a simple way to access all the contents of a collection.
6         [Pure]
7         [DispId(-4)]
8         IEnumerator GetEnumerator();
9     }
View Code

发现IEnumerable只有一个GetEnumerator函数,返回值是IEnumerator类型,从注释我们可以得知IEnumerable代表继承此接口的类可以获取一个IEnumerator来实现枚举这个类中包含的集合中的元素的功能(比如List<T>,ArrayList,Dictionary等继承了IEnumeratble接口的类)。

用foreach来了解IEnumerable,IEnumerator的工作原理

我们模仿ArrayList来实现一个简单的ConstArrayList,然后用foreach遍历。

 1 //一个常量的数组,用于foreach遍历
 2 class ConstArrayList : IEnumerable
 3 {
 4     public int[] constItems = new int[] { 1, 2, 3, 4, 5 };
 5     public IEnumerator GetEnumerator()
 6     {
 7         return new ConstArrayListEnumeratorSimple(this);
 8     }
 9 }
10 //这个常量数组的迭代器
11 class ConstArrayListEnumeratorSimple : IEnumerator
12 {
13     ConstArrayList list;
14     int index;
15     int currentElement;
16     public ConstArrayListEnumeratorSimple(ConstArrayList _list)
17     {
18         list = _list;
19         index = -1;
20     }
21 
22     public object Current
23     {
24         get
25         {
26             return currentElement;
27         }
28     }
29 
30     public bool MoveNext()
31     {
32         if(index < list.constItems.Length - 1)
33         {
34             currentElement = list.constItems[++index];
35             return true;
36         }
37         else
38         {
39             currentElement = -1;
40             return false;
41         }
42     }
43 
44     public void Reset()
45     {
46         index = -1;
47     }
48 }
49 class Program
50 {    
51     static void Main(string[] args)
52     {
53         ConstArrayList constArrayList = new ConstArrayList();
54         foreach(int item in constArrayList)
55         {
56             WriteLine(item);
57         }
58         ReadKey();
59     }
60 }
61 
62 输出结果:
63 1
64 2
65 3
66 4
67 5
View Code

代码达到了遍历效果,但是在用foreach遍历时,IEnumerator和IEnumerable究竟是如何运行的,我们可以通过增加增加日志可以直观的看到原因。

 1 //一个常量的数组,用于foreach遍历
 2 class ConstArrayList : IEnumerable
 3 {
 4     public int[] constItems = new int[] { 1, 2, 3, 4, 5 };
 5     public IEnumerator GetEnumerator()
 6     {
 7         WriteLine("GetIEnumerator");
 8         return new ConstArrayListEnumeratorSimple(this);
 9     }
10 }
11 //这个常量数组的迭代器
12 class ConstArrayListEnumeratorSimple : IEnumerator
13 {
14     ConstArrayList list;
15     int index;
16     int currentElement;
17     public ConstArrayListEnumeratorSimple(ConstArrayList _list)
18     {
19         list = _list;
20         index = -1;
21     }
22 
23     public object Current
24     {
25         get
26         {
27             WriteLine("Current");
28             return currentElement;
29         }
30     }
31 
32     public bool MoveNext()
33     {
34         if(index < list.constItems.Length - 1)
35         {
36             WriteLine("MoveNext true");   
37             currentElement = list.constItems[++index];
38             return true;
39         }
40         else
41         {
42             WriteLine("MoveNext false");
43             currentElement = -1;
44             return false;
45         }
46     }
47 
48     public void Reset()
49     {
50         WriteLine("Reset");
51         index = -1;
52     }
53 }
54 class Program
55 {    
56     static void Main(string[] args)
57     {
58         ConstArrayList constArrayList = new ConstArrayList();
59         foreach(int item in constArrayList)
60         {
61             WriteLine(item);
62         }
63         ReadKey();
64     }
65 }
66 输出结果:
67 GetIEnumerator
68 MoveNext true
69 Current
70 1
71 MoveNext true
72 Current
73 2
74 MoveNext true
75 Current
76 3
77 MoveNext true
78 Current
79 4
80 MoveNext true
81 Current
82 5
83 MoveNext false
View Code

通过输出结果,我们可以发现,foreach在运行时会先调用ConstArrayList的GetIEnumerator函数获取一个ConstArrayListEnumeratorSimple,之后通过循环调用ConstArrayListEnumeratorSimple的MoveNext函数,index后移,更新Current属性,然后返回Current属性,直到MoveNext返回false。

总结一下:
GetIEnumerator()负责获取枚举器。
MoveNext()负责让Current获取下一个值,并判断遍历是否结束。
Current负责返回当前指向的值。
Rest()负责重置枚举器的状态(在foreach中没有用到)
这些就是IEnumerable,IEnumerator的基本工作原理了。

其次我们发现:

ConstArrayList constArrayList = new ConstArrayList();
foreach(int item in constArrayList)
{
    writeLine(item);
}

其实就等价于:

ConstArrayList constArrayList = new ConstArrayList();
IEnumerator enumeratorSimple = constArrayList.GetEnumerator();
while (enumeratorSimple.MoveNext())
{
    int item = (int)enumeratorSimple.Current;
    WriteLine(item);
}

也就是说foreach其实是一种语法糖,用来简化对可枚举元素的遍历代码。而被遍历的类通过实现IEnumerable接口和一个相关的IEnumerator枚举器来实现遍历功能。

转载:https://www.cnblogs.com/blueberryzzz/p/8667261.html

 

 

posted @ 2018-10-23 10:08  fu1513  阅读(132)  评论(0编辑  收藏  举报