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

我们先思考几个问题:

  1. 为什么在foreach中不能修改item的值?
  2. 要实现foreach需要满足什么条件?
  3. 为什么Linq to Object中要返回IEnumerable?

接下来,先开始我们的正文。

自己实现迭代器

.net中迭代器是通过IEnumerable和IEnumerator接口来实现的,今天我们也来依葫芦画瓢。

首先来看看这两个接口的定义:

并没有想象的那么复杂。其中IEnumerable只有一个返回IEnumerator的GetEnumerator方法。而IEnumerator中有两个方法加一个属性。

接下来开发画瓢,我们继承IEnumerable接口并实现:

下面使用原始的方式调用:

有朋友开始说了,我们平时都是通过foreache来取值的,没有这样使用过啊。好吧,我们来使用foreach循环:

为什么说基本上是等效的呢?我们先看打印结果,在看反编译代码。

由此可见,两者有这么个关系:

我们可以回答第一个问题了“为什么在foreach中不能修改item的值?”:

我们还记得IEnumerator的定义吗

接口的定义就只有get没有set。所以我们在foreach中不能修改item的值。

我们再来回答第二个问题:“要实现foreach需要满足什么条件?”:

必须实现IEnumerable接口?NO

我们自己写的MyIEnumerable删掉后面的IEnumerable接口一样可以foreach(不信?自己去测试)。

所以要可以foreach只需要对象定义了GetEnumerator无参方法,并且返回值是IEnumerator或其对应的泛型。细看下图:

也就是说,只要可以满足这三步调用即可。不一定要继承于IEnumerable。有意思吧!下次面试官问你的时候一定要争个死去活来啊,哈哈!

yield的使用

你肯定发现了我们自己去实现IEnumerator接口还是有些许麻烦,并且上面的代码肯定是不够健壮。对的,.net给我们提供了更好的方式。

你会发现我们连MyIEnumerator都没要了,也可以正常运行。太神奇了。yield到底为我们做了什么呢?

好家伙,我们之前写的那一大坨。你一个yield关键字就搞定了。最妙的是这块代码:

这就是所谓的状态机吧!

我们继续来看GetEnumerator的定义和调用:

我们调用GetEnumerator的时候,看似里面for循环了一次,其实这个时候没有做任何操作。只有调用MoveNext的时候才会对应调用for循环:

现在我想可以回答你“为什么Linq to Object中要返回IEnumerable?”:

因为IEnumerable是延迟加载的,每次访问的时候才取值。也就是我们在Lambda里面写的where、select并没有循环遍历(只是在组装条件),只有在ToList或foreache的时候才真正去集合取值了。这样大大提高了性能。

如:

这个时候得到了就是IEnumerable对象,但是没有去任何遍历的操作。(对照上面的gif动图看)

什么,你还是不信?那我们再来做个实验,自己实现MyWhere:

现在看到了吧。执行到MyWhere的时候什么动作都没有(返回的就是IEnumerable),只有执行到ToList的时候才代码才真正的去遍历筛选。

这里的MyWhere其实可以用扩展方法来实现,提升逼格。(Linq的那些查询操作符就是以扩展的形式实现的)[了解扩展方法]。

怎样高性能的随机取IEnumerable中的值

这段代码来源《深入理解C#》,个人觉得非常妙。贴出来给大家欣赏哈。

个人对迭代器的理解和总结,不一定对。轻拍!

文章首链:http://www.cnblogs.com/zhaopei/p/5769782.html

demo下载:http://pan.baidu.com/s/1dE94c1b

感谢您的阅读。如果文章对您有用,那么请轻轻点个赞,以资鼓励。

时间: 2024-12-15 07:01:05

先说IEnumerable,我们每天用的foreach你真的懂它吗?的相关文章

农码一生博文索引

C#基础知识巩固 特性是什么东东 什么是反射.反射可以做些什么 依赖注入是什么? 可空类型Nullable<T>到底是什么鬼 谈扩展方法的理解 你必须知道的EF知识和经验 你知道C#中的Lambda表达式的演化过程吗? Linq表达式.Lambda表达式你更喜欢哪个? 先说IEnumerable,我们每天用的foreach你真的懂它吗?[推荐] 再讲IQueryable<T>,揭开表达式树的神秘面纱[推荐]

上周热点回顾(8.15-8.21)

热点随笔: · [无私分享:ASP.NET CORE 项目实战(第九章)]创建区域Areas,添加TagHelper(果冻布丁喜之郎)· 后台管理UI的选择(张果)· 先说IEnumerable,我们每天用的foreach你真的懂它吗?(农码一生)· [前端安全]JavaScript防http劫持与XSS(ChokCoco)· 为什么房间的 Wi-Fi 信号这么差(猫哥_kaiye)· [无私分享:ASP.NET CORE 项目实战(第十章)]发布项目到 Linux 上运行 Core 项目(果冻

EntityFramework IEnumerable,IQueryable ,Include

使用IQueryable using (var db = new CentaStaffEntities()) { IQueryable<Staff> queryablestaffs = db.Staff.AsQueryable().OrderBy(p=>p.StaffID).Skip(3).Take(3); foreach (var item in queryablestaffs) { Console.WriteLine(item.CnName); } } 使用IEnumerable u

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

IEnumerable枚举器接口的重要性,说一万句话都不过分.几乎所有集合都实现了这个接口,Linq的核心也依赖于这个万能的接口.C语言的for循环写得心烦,foreach就顺畅了很多. 我很喜欢这个接口,但在使用中也遇到不少的疑问,你是不是也有与我一样的困惑: (1) IEnumerable 与  IEnumerator到底有什么区别 (2) 枚举能否越界访问,越界访问是什么后果?为什么在枚举中不能改变集合的值? (3) Linq的具体实现到底是怎样的,比如Skip,它跳过了一些元素,那么这些

二、C++迭代器的两种实现方式 (Range for和C#、Java中的foreach)

一.迭代器概述 这个标题其实有点"标题党"的含义,因为C++在标准库中的实现迭代器的方式只有一种,也就是为类定义begin()和end()函数,C++11增加了range for语句,可以用来遍历迭代器中的元素.实现迭代器的第二种方式,就是用C++模拟C#和Java中的迭代器模式,并且我们可以定义出自己的foreach语句.除此之外,迭代器可能还有很多种实现的方法,各个库也会多自己的迭代器的实现有所定义,在这里只要明白迭代器的本质意义即可. 迭代器,也称作游标,是一种设计模式,我们可以

C#基础之IEnumerable

1.IEnumerable的作用 在使用Linq查询数据时经常以IEnumerable<T>来作为数据查询返回对象,在使用foreach进行遍历时需要该对象实现IEnumerable接口,这2个功能让我对IEnumerable充满了无穷的好奇.然而在VS中查看IEnumerable的定义时发现它只定义了一个GetEnumerator()方法,关于IEnumerator我知道它依靠MoveNext和Current来达到Foreach的遍历,但是具体是如何将数据进行迭代的,完整的流程是怎样的?这些

从yield关键字看IEnumerable和Collection的区别

C#的yield关键字由来以久,如果我没有记错的话,应该是在C# 2.0中被引入的.相信大家此关键字的用法已经了然于胸,很多人也了解yield背后的“延迟赋值”机制.但是即使你知道这个机制,你也很容易在不经意间掉入它制造的陷阱. 目录 一.一个很简单的例子 二.简单谈谈“延迟赋值” 三.从反射的代码帮助我们更加直接的了解yield导致的延迟赋值 四.如果需要“立即赋值”怎么办? 后记 一.一个很简单的例子 下面是一个很简单的例子:Vector为自定义表示二维向量的类型,Program的静态方法G

IList, ICollection ,IEnumerable AND IEnumerator in C#

IList, ICollection ,IEnumerable 很显然,这些都是集合接口的定义,先看看定义: 1 // 摘要: 2 // 表示可按照索引单独访问的对象的非泛型集合. 3 [ComVisible(true)] 4 public interface IList : ICollection, IEnumerable 5 { 6 7 bool IsFixedSize { get; } 8 9 bool IsReadOnly { get; } 10 11 object this[int i

IEnumerable和List有什么区别?(转)

如下 IList接口可以使用更多的方法.比如你看一个集合是否包含相应实体,IEnumerable不行,而IList里有Contains,相应的实现了IList的可以添加,删除相应实体.而IEnumerable不行. 但是这不是说IList就比IEnumerable好,就是因为IList实现的功能多,相对来说限制大了.你看Object,任何类都可用作Object,这就是因为他简单.同理,能为IList表达的数据集,一定能为IEnumerable表达,而能为IEnumerable表达不一定能为ILi