IEnumerable

饮水思源

《C#本质论》

Overview

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

foreach 和 数组

看下面的代码

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

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

  1.  
  1. string[] array = new string[]
  2. {
  3. "",
  4. "",
  5. ""
  6. };
  7. string[] array2 = array;
  8. for (int i = ; i < array2.Length; i++)
  9. {
  10. string value = array2[i];
  11. Console.WriteLine(value);
  12. }

在上面的代码中,奖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属性,提供了泛型的实现。

使用迭代器遍历集合

  1. static void Main(string[] args)
  2. {
  3. Stack<string> stack = new Stack<string>();
  4. stack.Push("");
  5. stack.Push("");
  6. stack.Push("");
  7. stack.Push("");
  8. stack.Push("");
  9.  
  10. IEnumerator<string> enumerator = stack.GetEnumerator();
  11. //使用迭代器遍历集合
  12. while (enumerator.MoveNext())
  13. {
  14. string value = enumerator.Current;
  15. Console.WriteLine(value);
  16. }
  17. }

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

  1. static void Main(string[] args)
  2. {
  3. Stack<string> stack = new Stack<string>();
  4. stack.Push("");
  5. stack.Push("");
  6. stack.Push("");
  7. stack.Push("");
  8. stack.Push("");
  9.  
  10. foreach (var item in stack)
  11. {
  12. Console.WriteLine(item);
  13. }
  14. }

foreach遍历反编译的IL代码

  1. .try
  2. {
  3. IL_004b: br.s IL_005e
  4. // loop start (head: IL_005e)
  5. IL_004d: ldloca.s
  6. IL_004f: call instance ! valuetype [System]System.Collections.Generic.Stack`/Enumerator<string>::get_Current()//获取Current只读属性
  7. IL_0054: stloc.
  8. IL_0055: nop
  9. IL_0056: ldloc.
  10. IL_0057: call void [mscorlib]System.Console::WriteLine(string)//输出
  11. IL_005c: nop
  12. IL_005d: nop
  13.  
  14. IL_005e: ldloca.s
  15. IL_0060: call instance bool valuetype [System]System.Collections.Generic.Stack`/Enumerator<string>::MoveNext()//调用MoveNext方法
  16. IL_0065: brtrue.s IL_004d
  17. // end loop
  18.  
  19. IL_0067: leave.s IL_0078
  20. } // end .try
  21. //回收资源
  22. finally
  23. {
  24. IL_0069: ldloca.s
  25. IL_006b: constrained. valuetype [System]System.Collections.Generic.Stack`/Enumerator<string>
  26. IL_0071: callvirt instance void [mscorlib]System.IDisposable::Dispose()
  27. IL_0076: nop
  28. IL_0077: endfinally
  29. } // end handler
  1.  

最终的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;">

 
 
 
 

IEnumerable<T>的更多相关文章

  1. 先说IEnumerable,我们每天用的foreach你真的懂它吗?

    我们先思考几个问题: 为什么在foreach中不能修改item的值? 要实现foreach需要满足什么条件? 为什么Linq to Object中要返回IEnumerable? 接下来,先开始我们的正 ...

  2. 为IEnumerable<T>添加RemoveAll<IEnumerable<T>>扩展方法--高性能篇

    最近写代码,遇到一个问题,微软基于List<T>自带的方法是public bool Remove(T item);,可是有时候我们可能会用到诸如RemoveAll<IEnumerab ...

  3. C# 索引器,实现IEnumerable接口的GetEnumerator()方法

    当自定义类需要实现索引时,可以在类中实现索引器. 用Table作为例子,Table由多个Row组成,Row由多个Cell组成, 我们需要实现自定义的table[0],row[0] 索引器定义格式为 [ ...

  4. Entity Framework中使用IEnumerable<T>、IQueryable<T>及IList<T>的区别

    1. IEnumerable<T> IEnumerable<T> :对于在内存中集合上运行的方法,返回的可枚举对象将捕获传递到方法的参数.在枚举该对象时,将使用查询运算符的逻辑 ...

  5. 你可能不知道的陷阱, IEnumerable接口

    1.  IEnumerable 与  IEnumerator IEnumerable枚举器接口的重要性,说一万句话都不过分.几乎所有集合都实现了这个接口,Linq的核心也依赖于这个万能的接口.C语言的 ...

  6. 通过IEnumerable和IDisposable实现可暂停和取消的任务队列

    一般来说,软件中总会有一些长时间的操作,这类操作包括下载文件,转储数据库,或者处理复杂的运算. 一种处理做法是,在主界面上提示正在操作中,有进度条,其他部分不可用.这里带来很大的问题, 使用者不知道到 ...

  7. 判断IEnumerable<T>集合中是否包含有T对象

    比如,有角色集合中,只有用户创建有角色,才出现“分配”铵钮.反之,隐藏. IEnumerable有一个方法,叫Any:

  8. .NET面试题系列[11] - IEnumerable<T>的派生类

    “你每次都选择合适的数据结构了吗?” - Jeffery Zhao .NET面试题系列目录 ICollection<T>继承IEnumerable<T>.在其基础上,增加了Ad ...

  9. .NET面试题系列[10] - IEnumerable的派生类

    .NET面试题系列目录 IEnumerable分为两个版本:泛型的和非泛型的.IEnumerable只有一个方法GetEnumerator.如果你只需要数据而不打算修改它,不打算为集合插入或删除任何成 ...

  10. .NET面试题系列[9] - IEnumerable

    .NET面试题系列目录 什么是IEnumerable? IEnumerable及IEnumerable的泛型版本IEnumerable<T>是一个接口,它只含有一个方法GetEnumera ...

随机推荐

  1. sking

    #include #include #include typedef struct SKI{ int data; int x; int y; char flag; int len; }STSKI; i ...

  2. linux常用端口查询

    0 | 无效端口,通常用于分析操作系统1 | 传输控制协议端口服务多路开关选择器2 | 管理实用程序3 | 压缩进程5 | 远程作业登录7 | 回显9 | 丢弃11 | 在线用户13 | 时间17 | ...

  3. [转载]Web API OData Inlinecount not working

    http://stackoverflow.com/questions/15422831/web-api-odata-inlinecount-not-working

  4. CSS3 - chrome,傲游,360极速浏览器不支持小于12px的字号的解决办法

    google流量器chrome,傲游,360极速浏览器都是基于webkit内核浏览器,默认不支持小于font-size小于12px 的字号,即定义font-size小于12px时会发现字体大小依然是1 ...

  5. <header><footer>引用

    示例:http://www.w3school.com.cn/html/html_layout.asp

  6. ant+sonar+jacoco代码质量代码覆盖率扫描

    使用ant构建的java web项目如何做sonar代码质量扫描?以下就是实际遇到并成功使用的案例一.做sonar扫描的准备工作    1.给web项目增加build.xml构建脚本.    2.下载 ...

  7. 10.29训练赛第一场B题

    题目大意:有n个队伍之间比赛,每两个队伍之间都有一场比赛,因此一共有n(n-1) / 2场比赛,但是这里丢失了一场比赛的记录,现在让你通过n(n-1) /2 -1场仍然存在的比赛记录来判断丢失的那条比 ...

  8. Anaconda+django写出第一个web app(九)

    今天来学习外键的使用,用外键来连接数据库中的两个表. 当我们的tutorials非常多的时候,目前的显示方式就会使得页面非常凌乱.我们可以考虑把这些教程分为不同的系列,页面只显示标题以及概要等信息,进 ...

  9. Ubuntu 14.04 + gnome session back (metacity) 任务栏右上角图标消失问题解决

    没错, 就是说右上角的所有图标 (时间啊, 系统啊所有的)都消失了. 通过下列命令可以恢复 dconf reset -f /org/gnome/gnome-panel/ 参考这篇帖子: Upgrade ...

  10. 跳过复制错误——slave_skip_errors、slave_exec_mode

    这一篇写写复制错误处理相关的另两个参数slave_skip_errors.slave_exec_mode,基本环境参考<复制错误处理——sql_slave_skip_counter> 一. ...