基础【循环】-----(枚举器)------(转)
Iterator:枚举器
如果你正在创建一个表现和行为都类似于集合的类,允许类的用户使用foreach语句对集合中的成员进行枚举将会是很方便的。这在C# 2.0中比 C# 1.1更容易实现一些。作为演示,我们先在 C# 1.1中为一个简单的集合添加枚举,然后我们修改这个范例,使用新的C#2.0 枚举构建方法。
我们将以创建一个简单化的List Box作为开始,它将包含一个8字符串的数组和一个整型,这个整型用于记录数组中已经添加了多少字符串。构造函数将对数组进行初始化并使用传递进来的参数填充它。
1 public ListBox(params string[] initialStrings) 2 { 3 strings = new String[8]; 4 5 foreach (string s in initialStrings) 6 { 7 strings[ctr++] = s; 8 } 9 }
View Code
除此以外,ListBox类还需要一个Add方法(进行添加 string 的操作) 和 一个返回数组中字符串个数的方法。
1 public void Add(string theString) 2 { 3 strings[ctr] = theString; 4 ctr++; 5 } 6 7 public int GetNumEntries() 8 { 9 return ctr; 10 }
View Code
NOTE:实际开发中,通常使用ArrayList,而不是固定大小的数组。在这里为了程序简单就没有做数组下标越界的检测。
从感觉上看,ListBox像是一个集合,如果可以使用集合中通常使用的 foreach 循环来获取listBox中的所有字符串将会是非常便利的。如此的话,可以这样书写代码:
1 ListBox lb = new ListBox("a", "b", "c", "d", "e", "f", "g", "h"); 2 foreach (string s in lb) { 3 Console.WriteLine(s); 4 }
View Code
但是,会得到这样一个错误:
“Iterator.ListBox”不包含“GetEnumerator”的公共定义,因此 foreach 语句不能作用于“Iterator.ListBox”类型的变量
1 public IEnumerator GetEnumerator() 2 { 3 return new ListBoxEnumerator(); 4 }
现在,ListBox 可以使用 foreach 循环了:
1 ListBox lbt = new ListBox("Hello", "World"); 2 3 lbt.Add("Who"); 4 lbt.Add("Is"); 5 lbt.Add("John"); 6 lbt.Add("Galt"); 7 8 foreach (string s in lbt) 9 { 10 Console.WriteLine("Value: {0}", s); 11 }
View Code
先是实例化这个ListBox ,并初始了两个字符串,随后又添加了四个。foreach循环接受ListBox实例,并且迭代它,依次返回字符串。输出是:
1 Hello 2 World 3 Who 4 Is 5 John 6 Galt
实现 IEnumerator 接口
注意到ListBoxEnumerator不仅需要实现IEnumerator接口,对于ListBox类它也需要一些特别了解;特别是,它必须可以获得ListBox的字符串数组并且遍历其所包含的字符串。IEnumerable 类和与其相关的 IEnumerator类之间的关系有一点微妙。实现IEnumerator接口的最好办法是在IEnumerable类里创建一个嵌套的IEnumerator类。
1 public class ListBox : IEnumerable 2 { 3 // 嵌套的私有ListBoxEnumerator类实现 4 private class ListBoxEnumerator : IEnumerator 5 { 6 // 代码实现... 7 } 8 // ListBox类的代码... 9 }
View Code
注意ListBoxEnumerator需要对它所嵌入的ListBox类的一个引用。你可以通过ListBoxEnumerator的构造函数来传递。
为了实现IEnumerator接口,ListBoxEnumerator需要两个方法:MoveNext和Reset,还有一个属性:Current。这些方法和属性的任务是创建一个状态机制,确保你可以在任何时候得知ListBox中的哪个元素是当前元素,并获得那个元素。
在这个例子中,这种状态机制是通过维护一个标明当前string的索引值来完成的,并且,你可以通过对外部类的string集合进行索引来返回这个当前的string。为了达到这个目标,你需要一个成员变量保存对于外部ListBox对象的引用,以及一个整型用于保存当前索引。
private ListBox lbt; private int index;
每次Reset方法被调用的时候,index被置为 -1。
1 public void Reset() 2 { 3 index = -1; 4 }
每次MoveNext被调用的时候,外部类的数组检查时候已经到了末尾,如果是这样,方法返回false。如果集合中还有对象,index将增加,并且方法返回true。
1 public bool MoveNext() 2 { 3 index++; 4 if (index >= lbt.strings.Length) 5 { 6 return false; 7 }else 8 { 9 return true; 10 } 11 }
View Code
最后,如果MoveNext方法返回True,foreach循环将调用Current属性。ListBoxEnumerator的Current属性的实现是索引外部类(ListBox)中的集合,并且返回找到的对象(这个例子中,是一个字符串)。注意,返回一个Object是因为IEnumerator接口中Current属性的签名如此。
1 public object Current 2 { 3 get { 4 return(lbt[index]); 5 } 6 }
View Code
在1.1中,所有想要通过foreach循环来迭代的类都需要实现IEnumerable接口,于是,必须创建一个实现了IEnumerator的类。最糟的是,enumerator返回的值并不是类型安全的。记得Current属性返回一个Object对象;它仅仅简单的假设你所返回的值与foreach循环所期望的相符合。
C# 2.0 的解救办法
使用C# 2.0 这些问题如同五月末的雪般融化了。在这个例子的2.0版本中,我重写上面的列表,使用C# 2.0的两个新特性:泛型 和 枚举器。
我以重新定义实现IEumerable<string>的ListBox作为开始:
public class ListBox : IEnumerable<string>
这样做确定这个类可以在foreach循环中使用,同时确保迭代的值是string类型。
现在,从上个例子中挪去整个嵌套类,并且用下面的代码替换 GetEnumerator方法。
1 public IEnumerator<string> GetEnumerator() 2 { 3 foreach (string s in strings) 4 { 5 yield return s; 6 } 7 }
View Code
GetEnumerator方法使用了新的 yield 语句。yield语句返回一个表达式。yield语句仅在迭代块中出现,并且返回foreach语句所期望的值。那也就是,对GetEnumerator的每次调用都将会产生集合中的下一个字符串;所有的状态管理已经都为你做好了!
就这样了,你已经完成了。不需要为每个类型实现你自己的enumerator,不需要创建嵌套类。你已经移除了至少30行代码,并且极大地简化了你的代码。程序继续像期望的那样运行,但是状态管理不再是你的任务,所有的都为你做好了。更进一步,由枚举器所返回的值一定是string类型,如果你想要返回其他类型,你可以修改IEnumerable泛型语句,IEnumerable泛型语句将反射新类型。
关于Yield的更多内容
作为对上一节的一些说明,应该告诉你:实际上,你可以在yield语句块中yield一个以上的值。这样,下面的语句是完全正确的C#语句:
1 public IEnumerator GetEnumerator() 2 { 3 yield return "Who"; 4 yield return " is"; 5 yield return "John Galt?"; 6 }
View Code
假设上面的代码位于一个名为foo的类中,你可以这样写:
1 foreach ( string s in new foo()) 2 { 3 Console.Write(s); 4 }
输出结果将会是:
Who is John Galt?
如果你现在停下来思考一下,这些也是之前的代码所做的事。它遍历了自己的foreach循环,并且产生出它所找到的每个string字符串。
转载于:https://www.cnblogs.com/shao-shao/articles/3447841.html
基础【循环】-----(枚举器)------(转)相关推荐
- Java心得--键值、枚举器
1. 集(Set):和数学上的"集合"概念相对应,是最简单的一种集合. Set集合中不区分元素的顺序,因此也就不记录元素的加入顺序. Set集合中不包含重复元素,即任意的两个元素e ...
- C#图解教程 第十八章 枚举器和迭代器
枚举器和迭代器 枚举器和可枚举类型 第12章中,我们看到可以用foreach语句遍历数组.在本章,我们会进一步探讨数组,来看看为什么它们可以被foreach语句处理.我们还会研究如何使用迭代器为用户自 ...
- WPF——专用枚举器ListBox和ComboBox
目录 介绍 提供了什么 本地定义的枚举器 代码 EnumItemList集合和EnumItem集合项 附加属性 使用代码 结束语 下载控件-141.8 KB 介绍 几天前,我发布了这篇文章,其中描述了 ...
- 一文详解枚举器和迭代器!
作者 | 喵叔 责编 | 胡巍巍 出品 | 程序人生(ID:coder_life) 今天来讲解一下开发人员会用但不理解的C#中的知识,这篇文章我们讲解一下枚举器与迭代器的知识. 枚举器 什么是枚举器? ...
- 黑马程序猿——C#枚举器深入解析
------- Java培训.Android培训.iOS培训..Net培训 .期待与您交流! ------- 废话不说了,上码,如果你可以一眼看穿下面这段代码的执行流程,请您就飘过吧(这段代码摘抄自 ...
- DRO:SFM任务中的深度循环优化器(阿里巴巴AI Lab)
代码.论文地址:在公众号「3D视觉工坊」,后台回复「DRO」,即可直接下载. Motivation: 解决一个优化问题,常见的优化器比如梯度下降法, 牛顿法等, 一般会先计算梯度------>再 ...
- 与基础事务管理器通讯失败
今天技术研发部告诉我 出现 与基础事务管理器通讯失败 的错误 ,经过排查,重启 IIS中的 应用程序池 解决了. 转载于:https://blog.51cto.com/mirwhite/64768 ...
- foreach遍历进阶_“枚举器“/GetEnumerator()方法
通过foreach遍历数据,实际上是调用了一个"枚举器"来遍历数据,和foreach没有任何关系,foreach只是语法上的简化而已,或者说foreach语句降低了枚举的复杂度. ...
- Java基础:类加载器
系列阅读 Java基础:类加载器 Java基础:反射 Java基础:注解 Java基础:动态代理 1. 什么是类加载器 类加载器就是用来加载类的东西!类加载器也是一个类:ClassLoader 类加载 ...
最新文章
- 计算机视觉的前沿应用,学术报告:计算机视觉应用前沿
- SharePoint:如何设置某个页面必须由站点集管理员打开
- VTK:小部件之ContourWidget
- python7.1处理异常
- 华为P50系列开始量产:Pro+版或进一步延期
- TypeSrcript如何引入第三方库 如果加d.ts以及async await如何使用 demo,只有代码,文字后续补充...
- adb查看某个文件是否存在_linux实现检查文件夹是否存在不存在则创建
- 微商怎么引流被加精准粉?微商有效引流被加方法
- B站视频下载助手使用教程
- 解决Linux系统不能上网问题
- matlab:双或三方演化博弈,lotka-Volterra 1.双方演化博弈:代分析稳定点分析,代绘制相位图,matlab仿真图代码
- RF-测试目录以及库引用、变量使用
- 蚂蚁金融加入以色列区块链隐私解决方案公司A轮融资
- sublimelinter php 语法不起作用,sublime安装插件sublimeLinter不起作用解决办法
- java编写分数加减法_JAVA 分数加减法
- 交易系统解析(六) -- 前台报盘应用设计要点
- 测试开发之Python核心笔记(15):迭代器与生成器
- pre 图像稳定_什么是图像稳定,它如何工作?
- 下周发布三维声呐Coda EchoScope的第四部视频
- python爬取蚂蜂窝帖子图片