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>的更多相关文章

  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. 开源.NET界面库

    一.十大开源的.NET用户界面框架 选择一款合适的GUI框架是.NET开发中比较重要但又很棘手的问题,因为用户界面相当于一款应用的"门面",直接面向用户.好的UI更能吸引用户,有时 ...

  2. WinFrom弹出输入框

    代码上面要引用 using Microsoft.VisualBasic; 还不够,在解决方案的引用那里,也要添加引用 如此,便可打出输入框了: ,); 5个参数分别的意思: 提示信息 标题 如果用户没 ...

  3. vue 使用v-cloak让在页面加载时不显示{{}}花括号

    官方说法: 这个指令保持在元素上直到关联实例结束编译. 和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准 ...

  4. 程序员与HR博弈之:有城府的表达你的兴趣爱好

    “面试”这个过程说简单其实也能很简单.譬如急需招某种技能的单位会因为你拥有某方面的经验或特长立马录取你,哪怕你其他方面表现的很“烂”. 从广义上来讲,很多公司尤其是大中型公司的招聘,并不是因为急缺某岗 ...

  5. iOS手势UIGestureRecognizer的使用及手势冲突的解决办法【转】

    转自:iOS开发中的手势体系——UIGestureRecognizer分析及其子类的使用 关于手势的一篇很好的帖子,转载过来免得丢失.你可能最感兴趣的是手势间的互斥处理,那么就搜索 4.手势间的互斥处 ...

  6. <dl>

    定义列表 自定义列表不仅仅是一列项目,而是项目及其注释的组合. 自定义列表以 <dl> 标签开始.每个自定义列表项以 <dt> 开始.每个自定义列表项的定义以 <dd&g ...

  7. 图文详解 解决 MVC4 Code First 数据迁移

    在使用Code first生成数据库后 当数据库发生更改时 运行程序就会出现数据已更改的问题  这时可以删除数据库重新生成解决 但是之前的数据就无法保留  为了保留之前的数据库数据  我们需要使用到C ...

  8. 直接读取修改exe文件

    1. 前言 配置器的编写有很多的方式,主要是直接修改原始的受控端的程序,有的方式是把受控端和配置信息都放到控制端程序的内部,在需要配置受控端的时候直接输入配置信息,生成受控端:也有的方式是在外部直接修 ...

  9. 【Python】HackBack(获取暴力破解服务器密码的IP来源)

    1.前言 又在0x00sec上翻到好东东. https://0x00sec.org/t/python-hackback-updated/882 帖子里的脚本会得到那些暴力服务器密码失败的IP和用户名, ...

  10. poj1679

    题意:给定一个无向连通图,问该图的最小生成树是否唯一. 分析:有一个定理,如果该图存在次小生成树(与原最小生成树不同,但长度小于等于原最小生成树),则一定可以通过从原最小生成树中去掉一个边并再入一个边 ...