首页 技术 正文
技术 2022年11月22日
0 收藏 328 点赞 3,606 浏览 1547 个字

我们先思考几个问题:

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

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

自己实现迭代器

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

我们还记得IEnumerator的定义吗

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

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

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

必须实现IEnumerable接口?NO

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

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

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

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

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

yield的使用

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

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

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

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

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

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

这就是所谓的状态机吧!

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

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

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

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

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

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

如:

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

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

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

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

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

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

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

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

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

结束:

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

接下篇:《再讲IQueryable<T>,揭开表达式树的神秘面纱

本文以同步至《C#基础知识巩固系列

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,104
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,581
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,428
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,200
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,835
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,918