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 }
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 }
发现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
代码达到了遍历效果,但是在用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
通过输出结果,我们可以发现,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