博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
IEnumerable<T>
阅读量:6451 次
发布时间:2019-06-23

本文共 3341 字,大约阅读时间需要 11 分钟。

IEnumerable

饮水思源

《C#本质论》

Overview

根据定义,.Net 的中集合,本质上是一个类,它最起码实现了IEnumeraable 或者非泛型的IEnumerable 结构。 这个接口非常关键,如果想要支持对集合执行的遍历操作,子起码的要求就是实现由IEnumerable 规定的方法。

foreach 和 数组

看下面的代码

string[] strArr = { "1", "2", "3" };foreach (var item in strArr){    Console.WriteLine(item);}

上面的代码想必已经非常的熟悉了,我们来看一下上面的代码究竟是什么样的

 
string[] array = new string[]{    "1",    "2",    "3"};string[] array2 = array;for (int i = 0; i < array2.Length; i++){    string value = array2[i];    Console.WriteLine(value);}

 

在上面的代码中,奖foreach编译为了for循环,这就需要依赖于数组的Length 属性来确定for循环的次数了。

foreach 和 IEnumerable

因为数据的长度是固定的,并且可以使用索引操作符,这样可以通过for循环的形式来达到遍历数组的目的,但是对IEnumerable 接口的集合,可能就不太适用了,比如说: stack<T> Queue<T> 等等,是不支持按照索引获取元素的,所以要有一个更加普适的方式来达到遍历的目的,这种方式就是迭代器 (iterator) 模式 , 它只需要确定,集合的第一个元素,下一个元素,和最后一个元素就能达到遍历的目的。

System.Collections.Generic.IEnumerator 和 System.Collections.IEnumerator 接口,分别实现了,泛型和非泛型的迭代器模式,可以使用迭代器模式遍历集合,而不是通过索引操作符来遍历。

继承层次类图

其中,IEnumerator 中三个成员, 只读属性object Current 代表当前的元素,方法bool MoveNext() 将元素移动到下一个元素,同时检测是否枚举完成了集合中的每一个元素。 void Reset() 方法永远不要主动调用他 , 该方法会抛出一个 NotImplemented Exception 异常,IEnumerator 重载了Current属性,提供了泛型的实现。

使用迭代器遍历集合

static void Main(string[] args){    Stack
stack = new Stack
(); stack.Push("1"); stack.Push("2"); stack.Push("3"); stack.Push("4"); stack.Push("5"); IEnumerator
enumerator = stack.GetEnumerator(); //使用迭代器遍历集合 while (enumerator.MoveNext()) { string value = enumerator.Current; Console.WriteLine(value); }}

 

当然使用While循环遍历的太麻烦了

static void Main(string[] args){    Stack
stack = new Stack
(); stack.Push("1"); stack.Push("2"); stack.Push("3"); stack.Push("4"); stack.Push("5"); foreach (var item in stack) { Console.WriteLine(item); }}

 

foreach遍历反编译的IL代码

.try{    IL_004b: br.s IL_005e    // loop start (head: IL_005e)        IL_004d: ldloca.s 1        IL_004f: call instance !0 valuetype [System]System.Collections.Generic.Stack`1/Enumerator
::get_Current()//获取Current只读属性 IL_0054: stloc.2 IL_0055: nop IL_0056: ldloc.2 IL_0057: call void [mscorlib]System.Console::WriteLine(string)//输出 IL_005c: nop IL_005d: nop IL_005e: ldloca.s 1 IL_0060: call instance bool valuetype [System]System.Collections.Generic.Stack`1/Enumerator
::MoveNext()//调用MoveNext方法 IL_0065: brtrue.s IL_004d // end loop IL_0067: leave.s IL_0078} // end .try//回收资源finally{ IL_0069: ldloca.s 1 IL_006b: constrained. valuetype [System]System.Collections.Generic.Stack`1/Enumerator
IL_0071: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0076: nop IL_0077: endfinally} // end handler

 

 

最终的IL代码还是会编译成类似While循环的方式,foreach循环只不是帮助我们减少了不必要的代码量。

状态共享

请思考这么一个问题,上述的代码,如果循环的时候,再次嵌套一个循环再次访问stack 集合,那么,MoveNext方法会不会乱套呢?

答案是不会的? 请注意,在上面的类图中我们的集合类是没有直接的继承自IEnumerator<T> 接口的,而是继承了IEnumerable 接口,这个接口仅仅提供了一个方法,那就是获取IEnumerator<T> 对象,MoveNext() 方法的状态,不是有集合类来维持的,而是由类外一个类来维持状态的,这个类通常是一个内部类,以方便访问集合的成员。

清理状态

由于由实现了IEnumerator<T> 接口的类来维护状态,有时候在遍历完集合或者引发的异常的时候有时候需要清理一下状态,为了这种考虑,IEnumerator 集合还实现了,

<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">

 
 
 
 

转载于:https://www.cnblogs.com/slyfox/p/8653255.html

你可能感兴趣的文章
Linux学习总结 (未完待续...)
查看>>
NoSQL数据库探讨 - 为什么要用非关系数据库?
查看>>
String字符串的截取
查看>>
switch函数——Gevent源码分析
查看>>
Spring MVC简单原理
查看>>
DynamoDB Local for Desktop Development
查看>>
ANDROID的SENSOR相关信息
查看>>
laravel 使用QQ邮箱发送邮件
查看>>
用javascript验证哥德巴赫猜想
查看>>
Shell编程-环境变量配置文件
查看>>
thymeleaf 中文乱码问题
查看>>
(转)CSS浮动(float,clear)通俗讲解
查看>>
os.walk函数
查看>>
[Unity3d]DrawCall优化手记
查看>>
细数.NET 中那些ORM框架 —— 谈谈这些天的收获之一
查看>>
SQL Serever学习7——数据表2
查看>>
洛谷——P2404 自然数的拆分问题
查看>>
(转)Mac 下设置android NDK的环境
查看>>
[struts]s:action 的使用方法
查看>>
dubbo问题总结
查看>>