IEnumerator和IEnumerable详解

IEnumerator和IEnumerable详解

 

IEnumerator和IEnumerable

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

public interface IEnumerator
    {
        // Interfaces are not serializable
        // Advances the enumerator to the next element of the enumeration and // returns a boolean indicating whether an element is available. Upon // creation, an enumerator is conceptually positioned before the first // element of the enumeration, and the first call to MoveNext // brings the first element of the enumeration into view. // bool MoveNext(); // Returns the current element of the enumeration. The returned value is // undefined before the first call to MoveNext and following a // call to MoveNext that returned false. Multiple calls to // GetCurrent with no intervening calls to MoveNext // will return the same object. // Object Current { get; } // Resets the enumerator to the beginning of the enumeration, starting over. // The preferred behavior for Reset is to return the exact same enumeration. // This means if you modify the underlying collection then call Reset, your // IEnumerator will be invalid, just as it would have been if you had called // MoveNext or Current. // void Reset(); }

IEnumerable:

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

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

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

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

//一个常量的数组,用于foreach遍历
class ConstArrayList : IEnumerable
{
    public int[] constItems = new int[] { 1, 2, 3, 4, 5 }; public IEnumerator GetEnumerator() { return new ConstArrayListEnumeratorSimple(this); } } //这个常量数组的迭代器 class ConstArrayListEnumeratorSimple : IEnumerator { ConstArrayList list; int index; int currentElement; public ConstArrayListEnumeratorSimple(ConstArrayList _list) { list = _list; index = -1; } public object Current { get { return currentElement; } } public bool MoveNext() { if(index < list.constItems.Length - 1) { currentElement = list.constItems[++index]; return true; } else { currentElement = -1; return false; } } public void Reset() { index = -1; } } class Program { static void Main(string[] args) { ConstArrayList constArrayList = new ConstArrayList(); foreach(int item in constArrayList) { WriteLine(item); } ReadKey(); } }

输出结果:
1
2
3
4
5

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

//一个常量的数组,用于foreach遍历
class ConstArrayList : IEnumerable
{
    public int[] constItems = new int[] { 1, 2, 3, 4, 5 }; public IEnumerator GetEnumerator() { WriteLine("GetIEnumerator"); return new ConstArrayListEnumeratorSimple(this); } } //这个常量数组的迭代器 class ConstArrayListEnumeratorSimple : IEnumerator { ConstArrayList list; int index; int currentElement; public ConstArrayListEnumeratorSimple(ConstArrayList _list) { list = _list; index = -1; } public object Current { get { WriteLine("Current"); return currentElement; } } public bool MoveNext() { if(index < list.constItems.Length - 1) { WriteLine("MoveNext true"); currentElement = list.constItems[++index]; return true; } else { WriteLine("MoveNext false"); currentElement = -1; return false; } } public void Reset() { WriteLine("Reset"); index = -1; } } class Program { static void Main(string[] args) { ConstArrayList constArrayList = new ConstArrayList(); foreach(int item in constArrayList) { WriteLine(item); } ReadKey(); } }

输出结果:
GetIEnumerator
MoveNext true
Current
1
MoveNext true
Current
2
MoveNext true
Current
3
MoveNext true
Current
4
MoveNext true
Current
5
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/cjm123/p/8671783.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值