对[foreach]的浅究到发现[yield]

  闲来无事,翻了翻以前的代码,做点总结,菜鸟从这里起航,呵呵。

一、List的foreach遍历

  先上代码段[1]:


 1  class Program
2 {
3 static void Main(string[] args)
4 {
5 List<string> dayList = new List<string> { "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat" };
6 foreach (var day in dayList)
7 {
8 Console.WriteLine(day);
9 }
10 Console.ReadKey();
11 }
12 }

  这是我们经常用的,简单明了,这里就不赘述了。

二、对List的浅究

  接着我就产生了疑问,List具有怎样的特性才使得foreach可以对它进行遍历呢?这个遍历是如何实现的?

  下面就来浅究,再上代码段[2]:


 1     public class DaysList<T> : System.Collections.IEnumerable
2 {
3 T[] daysArry;
4 public DaysList(T[] days)
5 {
6 daysArry = days;
7 }
8 public System.Collections.IEnumerator GetEnumerator()
9 {
10 for (int i = 0; i < daysArry.Length; i++)
11 {
12 yield return daysArry[i];
13 }
14 }
15 }
16
17 class Program
18 {
19 static void Main(string[] args)
20 {
21 string[] days = { "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat" };
22 var daysList = new DaysList<string>(days);
23
24 foreach (string day in daysList)
25 {
26 System.Console.WriteLine(day);
27 }
28 Console.ReadKey();
29 }
30 }

  通过查阅我们发现LIst是通过实现System.Collections.IEnumerable接口来达到可以被遍历的能力,而实现System.Collections.IEnumerable接口必须实现它里面的GetEnumerator()方法,用来返回一个循环访问集合的枚举器,代码段[2]中就有我对GetEnumerator()方法的实现,其中有个关键字[yield]不知大家注意到没。

  我的理解是:与其说是foreach遍历List,不如说是foreach遍历的是List中的GetEnumerator()方法返回的枚举器,注意这个枚举器实现了IEnumerator
接口,(插句话,IEnumerable接口标识某个类具备被遍历的能力,而IEnumerator
接口则使某个类真正具备这个能力!)。而当foreach对List进行循环遍历时,每个循环就是通过[yield]来分隔的。

三、对foreach的浅究

  通过标题二,我们大概对List进行了了解,但不清楚,下面看看foreach。

  依旧代码段[3]:


 1     //注意:其中类DaysList<T>的实现同代码段[2]一样
2 //这里只展示foreach的实现。
3 class Program
4 {
5 static void Main(string[] args)
6 {
7 string[] days = { "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat" };
8 var daysList = new DaysList<string>(days);
9
10 System.Collections.IEnumerator enumertor = daysList.GetEnumerator();
11 while (enumertor.MoveNext())
12 {
13 System.Console.WriteLine(enumertor.Current);
14 }
15 Console.ReadLine();
16 }
17 }

  就像上面说的,foreach其实遍历的是List中的GetEnumerator()方法返回的枚举器enumertor,而这个枚举器所实现的接口IEnumerator中定义了只读的Current属性(用来获取枚举器当前的所指的集合中的元素)、MoveNext方法(将枚举器推进到集合中的下一个元素,返回值代表是否到了集合末尾)、Reset方法(使枚举器指到集合第一个元素之前,也就是重置枚举器),明白了这些,我们就可以像代码段[3]中一样通过[while]语法来实现跟foreach一样的功能了,而上文中的[yield]关键字浅显的理解就是用来划分要遍历的集合中的每个元素的。

  最后,本来还想分析分析这个实现了IEnumerator接口的枚举器是怎么生成的,想象一下它的内部实现,应该很有意思!

  就这样吧,下班了,大家共勉!

对[foreach]的浅究到发现[yield],布布扣,bubuko.com

时间: 2024-11-05 17:25:32

对[foreach]的浅究到发现[yield]的相关文章

对[yield]的浅究到发现[async][await]

上篇对[foreach]的浅究到发现[yield]写完后,觉得对[yield]还没有理解清楚,想起曾经看过一位大牛的帖子讲的很深刻(链接在此),回顾了下,在这里写出自己的理解,与各位分享. 一.通常的异步 现在我们假设一种平时经常遇到的情况,现有三个方法,其中funcOne和funcTwo比较耗时需要异步执行,而且他们的逻辑是必须在funcOne执行完后才可以执行funcTwo,同理funcTwo执行完后才能执行funcThree. 按照这样的设定,通常的做法请看代码段[1]: 1 public

关于php foreach的浅谈

前两天在看鸟哥的微博的时候发现了一个很有趣的问题关于PDO类的使用中有个buildparam这个方法的问题. 就是在我们执行这个方法中假设有两个key-value对中分别有两个值$arr = array('weibo'=>'weibo','niaoge'=>'niaoge'); 假设在foreach($arr=>$v){ $brr["weibo"] = $v["weibo"]; $brr["niaoge"] = $v["

【Python学习笔记之二】浅谈Python的yield用法

在上篇[Python学习笔记之一]Python关键字及其总结中我提到了yield,本篇文章我将会重点说明yield的用法 在介绍yield前有必要先说明下Python中的迭代器(iterator)和生成器(constructor). 一.迭代器(iterator) 在Python中,for循环可以用于Python中的任何类型,包括列表.元祖等等,实际上,for循环可用于任何“可迭代对象”,这其实就是迭代器 迭代器是一个实现了迭代器协议的对象,Python中的迭代器协议就是有next方法的对象会前

浅究JavaBean规范

JavaBean规范 JavaBean是一种符合一定规范的Java类.分为业务Bean类和实体Bean类. 实体 Bean 的主要作用是用来对系统中要用到的数据进行封装,这些数据包括用户从前台的输入和从数据库中读取的数据等等,是 JavaBean 中较简单的一类. 这里谈一下实体Bean. (1)JavaBean类必须是public类.我认为需要给其它类使用它的权利,因此能反衬出它的封装性能. (2)JavaBean类必须有一个不带参数的空的公用构造器.默认的构造器也可以,因为默认构造器的访问权

字典查找、linq、foreach、yield等几种查找性能对比

先上代码,以1千万记录的内存查找测试: List<Student> stuList = new List<Student>(); Dictionary<int, Student> dictStu = new Dictionary<int, Student>(); Student student = null; for (int i = 0; i < 10000000; i++) { student = new Student(i); stuList.A

浅谈inline-block

一.区分block,inline,inline-block 1.block block元素会独占一行,多个block元素会各自新起一行.默认情况下,block元素宽度自动填满其父元素宽度. block元素可以设置width,height属性.块级元素即使设置了宽度,仍然是独占一行. block元素可以设置margin和padding属性. 2.inline inline元素不会独占一行,多个相邻的行内元素会排列在同一行里,直到一行排列不下,才会新换一行,其宽度随元素的内容而变化. inline元

foreach退出循环(新人请多多关照~)

今天做一个关于人员信息修改的页面时,我用foreach获取数据库数据时发现,用if else判断输入的内容时,会一个一个的做对比,导致错误提醒时会弹出与数据库内容行数相同条的提醒,最后发现将数据直接命为全局变量便轻松的解决了这个问题. var totaldata; $(function(){ $("#cha").click(function() { validateForm(); }); }); /** * 修改内容判断 */ function validateForm(){ var

新注册第一帖----------------------乱码新手自学.net 之Linq 入门篇

作为一个业余开发,断断续续学.net/c#也有不少日子了, 学习过程中,不断忘了学,学了忘,这让我很苦恼. 以前学习过程中,我总是在笔记本中记录下来知识要点,这么久下来,笔记本都写了四五本了. 然而,随着笔记本的增多,自己很快发现,笔记写了跟没写一样:笔记多了就找不到了-- 所以,我觉得还是上博客园写博客,记录自己每天的学习心得. 如果有什么错误的地方,欢迎大神指教,小弟在这给大神跪谢了 ======================================================

C#各版本新特性

转 C# 2.0 泛型(Generics) 泛型是CLR 2.0中引入的最重要的新特性,使得可以在类.方法中对使用的类型进行参数化. 例如,这里定义了一个泛型类: class MyCollection<T> { T variable1; private void Add(T param){ } } 使用的时候:MyCollection<string> list2 = new MyCollection<string>(); MyCollection<Object&g