IEnumerable<T>
IEnumerable
饮水思源
《C#本质论》
Overview
根据定义,.Net 的中集合,本质上是一个类,它最起码实现了IEnumeraable 或者非泛型的IEnumerable 结构。 这个接口非常关键,如果想要支持对集合执行的遍历操作,子起码的要求就是实现由IEnumerable 规定的方法。
foreach 和 数组
看下面的代码
string[] strArr = { "", "", "" };
foreach (var item in strArr)
{
Console.WriteLine(item);
}
上面的代码想必已经非常的熟悉了,我们来看一下上面的代码究竟是什么样的
string[] array = new string[]
{
"",
"",
""
};
string[] array2 = array;
for (int i = ; 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<string> stack = new Stack<string>();
stack.Push("");
stack.Push("");
stack.Push("");
stack.Push("");
stack.Push(""); IEnumerator<string> enumerator = stack.GetEnumerator();
//使用迭代器遍历集合
while (enumerator.MoveNext())
{
string value = enumerator.Current;
Console.WriteLine(value);
}
}
当然使用While循环遍历的太麻烦了
static void Main(string[] args)
{
Stack<string> stack = new Stack<string>();
stack.Push("");
stack.Push("");
stack.Push("");
stack.Push("");
stack.Push(""); 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
IL_004f: call instance ! valuetype [System]System.Collections.Generic.Stack`/Enumerator<string>::get_Current()//获取Current只读属性
IL_0054: stloc.
IL_0055: nop
IL_0056: ldloc.
IL_0057: call void [mscorlib]System.Console::WriteLine(string)//输出
IL_005c: nop
IL_005d: nop IL_005e: ldloca.s
IL_0060: call instance bool valuetype [System]System.Collections.Generic.Stack`/Enumerator<string>::MoveNext()//调用MoveNext方法
IL_0065: brtrue.s IL_004d
// end loop IL_0067: leave.s IL_0078
} // end .try
//回收资源
finally
{
IL_0069: ldloca.s
IL_006b: constrained. valuetype [System]System.Collections.Generic.Stack`/Enumerator<string>
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;">
IEnumerable<T>的更多相关文章
- 先说IEnumerable,我们每天用的foreach你真的懂它吗?
我们先思考几个问题: 为什么在foreach中不能修改item的值? 要实现foreach需要满足什么条件? 为什么Linq to Object中要返回IEnumerable? 接下来,先开始我们的正 ...
- 为IEnumerable<T>添加RemoveAll<IEnumerable<T>>扩展方法--高性能篇
最近写代码,遇到一个问题,微软基于List<T>自带的方法是public bool Remove(T item);,可是有时候我们可能会用到诸如RemoveAll<IEnumerab ...
- C# 索引器,实现IEnumerable接口的GetEnumerator()方法
当自定义类需要实现索引时,可以在类中实现索引器. 用Table作为例子,Table由多个Row组成,Row由多个Cell组成, 我们需要实现自定义的table[0],row[0] 索引器定义格式为 [ ...
- Entity Framework中使用IEnumerable<T>、IQueryable<T>及IList<T>的区别
1. IEnumerable<T> IEnumerable<T> :对于在内存中集合上运行的方法,返回的可枚举对象将捕获传递到方法的参数.在枚举该对象时,将使用查询运算符的逻辑 ...
- 你可能不知道的陷阱, IEnumerable接口
1. IEnumerable 与 IEnumerator IEnumerable枚举器接口的重要性,说一万句话都不过分.几乎所有集合都实现了这个接口,Linq的核心也依赖于这个万能的接口.C语言的 ...
- 通过IEnumerable和IDisposable实现可暂停和取消的任务队列
一般来说,软件中总会有一些长时间的操作,这类操作包括下载文件,转储数据库,或者处理复杂的运算. 一种处理做法是,在主界面上提示正在操作中,有进度条,其他部分不可用.这里带来很大的问题, 使用者不知道到 ...
- 判断IEnumerable<T>集合中是否包含有T对象
比如,有角色集合中,只有用户创建有角色,才出现“分配”铵钮.反之,隐藏. IEnumerable有一个方法,叫Any:
- .NET面试题系列[11] - IEnumerable<T>的派生类
“你每次都选择合适的数据结构了吗?” - Jeffery Zhao .NET面试题系列目录 ICollection<T>继承IEnumerable<T>.在其基础上,增加了Ad ...
- .NET面试题系列[10] - IEnumerable的派生类
.NET面试题系列目录 IEnumerable分为两个版本:泛型的和非泛型的.IEnumerable只有一个方法GetEnumerator.如果你只需要数据而不打算修改它,不打算为集合插入或删除任何成 ...
- .NET面试题系列[9] - IEnumerable
.NET面试题系列目录 什么是IEnumerable? IEnumerable及IEnumerable的泛型版本IEnumerable<T>是一个接口,它只含有一个方法GetEnumera ...
随机推荐
- gdb调试2—单步执行和跟踪函数
int add_range(int low, int high); int main(int argc, char *argv[]) { int result[100]; result[0] = ad ...
- IIS发布MVC出错
一个MVC网站在发布到IIS上时,出现了这个问题: 然后解决办法: 然后应用程序池那里,自己点右键添加一个 新建完应用池之后选中点高级设置 最后,添加网站,添加网站的时候应用程序池选择自己刚刚新建的那 ...
- 查找和替换img src
$("#imgId")[0].src; //获取 $("#imgId").attr('src',path); //修改
- 用yaml来编写配置文件
yaml是一个数据序列化的标准,适用于所有开发语言,最大的特点是可读性好. yaml的一个主要应用方向就是编写配置文件,有非常多的系统和框架采用yaml进行配置. yaml有以下基本规则: 1.大小写 ...
- Linux-Xshell会话保持
1.Xshell客户端设置 2.服务端设置 修改/etc/ssh/sshd_config文件,找到 ClientAliveInterval 0和ClientAliveCountMax 3并将注释符号( ...
- 《Linux命令行与shell脚本编程大全》第十一章 构建基本脚本
11.1使用多个命令 $date;who // 命令列表,加入分号就可以,这样会依次执行.参见5.2.1节 注意区分$(date;who),这个是进程列表,会生成一个子shell来执行 Shel ...
- WordPress中使用Markdown和Syntax Highlighter
下载安装插件 在wordpress插件中安装WP Code Prettify. PHP Markdown Extra 下载Extra,并上传安装到wordpress. Code Prettify th ...
- 【转】java comparator 升序、降序、倒序从源码角度理解
原文链接:https://blog.csdn.net/u013066244/article/details/78997869 环境jdk:1.7+ 前言之前我写过关于comparator的理解,但是都 ...
- iis6 和iis7s上整个网站重定向
iis6 和iis7s上整个网站重定向 重定向作用: 重定向(Redirect)就是通过各种方法将各种网络请求重新定个方向转到其它位置.举例说明:就像我XX公司,之前用的网络域名是“www.bb. ...
- ThinkPHP中的统计查询方法
• count() 表示查询表中总的记录数 • max() 表示查询某个字段的最大值 • min() 表示查询某个字段的最小值 • avg() 表示查询某个字段的平均值 • sum() 表示求出某个字 ...