foreach遍历扩展(二)
一、前言
假设存在一个数组,其遍历模式是根据索引进行遍历的;又假设存在一个HashTable,其遍历模式是根据键值进行遍历的;无论哪种集合,如果它们的遍历没有一个共同的接口,那么在客户端进行调用的时候,就需要对每种集合的具体类型进行它们各自的具体代码编写,当需求发生变化时,就必须修改我们的代码。并且客户端过多的关注集合内部的实现,代码的移植性就会变差,违反了开闭原则,这个时候迭代器就诞生了,现在我们来根据上一章 foreach遍历原理(一)实现我们自己的迭代器。
二、代码示例
class Program { static void Main(string[] args) { //使用接口IMyEnumerable代替MyList IMyEnumerable list = new MyList(); //得到迭代器,在循环中针对迭代器编码,而不是集合MyList IMyEnumerator enumerator = list.GetEnumerator(); while (enumerator.MoveNext()) { object current = enumerator.Current; Console.WriteLine(current); } Console.ReadKey(); } /// <summary> /// 要求所有的迭代器全部实现该接口 /// </summary> interface IMyEnumerator { bool MoveNext(); object Current { get; } } /// <summary> /// 要求所有的集合实现该接口 /// 这样一来,客户端就可以针对该接口编码, /// 而无须关注具体的实现 /// </summary> interface IMyEnumerable { IMyEnumerator GetEnumerator(); int Count { get; } } class MyList : IMyEnumerable { ]{,,,,,,,,,}; IMyEnumerator myEnumerator; public object this[int i] { get { return items[i]; } set { this.items[i] = value; } } public int Count { get { return items.Length; } } public IMyEnumerator GetEnumerator() { if (myEnumerator == null) { myEnumerator = new MyEnumerator(this); } return myEnumerator; } } class MyEnumerator : IMyEnumerator { ; MyList myList; public MyEnumerator(MyList myList) { this.myList = myList; } public bool MoveNext() { > myList.Count) { index = ; return false; } else { index++; return true; } } public object Current { ]; } } } }
运行结果:
三、疑问——为什么不把 IMyEnumerable 和 IMyEnumerator 接口写在同一个接口里面,例如新建一个接口名字为 IForeach,不使用迭代器,也能输出上面的结果
class Program { static void Main(string[] args) { IForeach iForeach = new MyList(); while (iForeach.MoveNext()) { object current = iForeach.Current; Console.WriteLine(current); } Console.ReadKey(); } interface IForeach { bool MoveNext(); object Current { get; } int Count { get; } } class MyList : IForeach { ] { , , , , , , , , , }; ; public object this[int i] { get { return items[i]; } set { this.items[i] = value; } } public int Count { get { return items.Length; } } public bool MoveNext() { > Count) { index = ; return false; } else { index++; return true; } } public object Current { ]; } } } }
四、如果我现在有新需求要倒序输出,那么按照上面“三”的做法,就必须修改 MyList 类里面的代码,而且是大改。如果使用迭代器 ,我们只需要重新写类继承迭代器IMyEnumerator,替换一下迭代器,就能够实现MyList 的倒序输出。
新增一个倒序迭代器
class MyInvertEnumerator : IMyEnumerator { ; MyList myList; public MyInvertEnumerator(MyList myList) { this.myList = myList; index = myList.Count; } public bool MoveNext() { <) { index = myList.Count; return false; } else { index--; return true; } } public object Current { get { return myList[index]; } } }
修改MyList集合里面获取迭代器的方法,然后运行就能够
class MyList : IMyEnumerable { ]{,,,,,,,,,}; IMyEnumerator myEnumerator; public object this[int i] { get { return items[i]; } set { this.items[i] = value; } } public int Count { get { return items.Length; } } public IMyEnumerator GetEnumerator() { if (myEnumerator == null) { // myEnumerator = new MyEnumerator(this);//正序输出 myEnumerator = new MyInvertEnumerator(this);//倒序输出 } return myEnumerator; } }
倒序输出完整代码:
class Program { static void Main(string[] args) { //使用接口IMyEnumerable代替MyList IMyEnumerable list = new MyList(); //得到迭代器,在循环中针对迭代器编码,而不是集合MyList IMyEnumerator enumerator = list.GetEnumerator(); while (enumerator.MoveNext()) { object current = enumerator.Current; Console.WriteLine(current); } Console.ReadKey(); } /// <summary> /// 要求所有的迭代器全部实现该接口 /// </summary> interface IMyEnumerator { bool MoveNext(); object Current { get; } } /// <summary> /// 要求所有的集合实现该接口 /// 这样一来,客户端就可以针对该接口编码, /// 而无须关注具体的实现 /// </summary> interface IMyEnumerable { IMyEnumerator GetEnumerator(); int Count { get; } } class MyList : IMyEnumerable { ]{,,,,,,,,,}; IMyEnumerator myEnumerator; public object this[int i] { get { return items[i]; } set { this.items[i] = value; } } public int Count { get { return items.Length; } } public IMyEnumerator GetEnumerator() { if (myEnumerator == null) { // myEnumerator = new MyEnumerator(this);//正序输出 myEnumerator = new MyInvertEnumerator(this);//倒序输出 } return myEnumerator; } } class MyEnumerator : IMyEnumerator { ; MyList myList; public MyEnumerator(MyList myList) { this.myList = myList; } public bool MoveNext() { > myList.Count) { index = ; return false; } else { index++; return true; } } public object Current { ]; } } } class MyInvertEnumerator : IMyEnumerator { ; MyList myList; public MyInvertEnumerator(MyList myList) { this.myList = myList; index = myList.Count; } public bool MoveNext() { <) { index = myList.Count; return false; } else { index--; return true; } } public object Current { get { return myList[index]; } } } }
迭代器就讲到这里了,谢谢大家。
foreach遍历扩展(二)的更多相关文章
- c#--foreach遍历的用法与split的用法
一. foreach循环用于列举出集合中所有的元素,foreach语句中的表达式由关键字in隔开的两个项组成.in右边的项是集合名,in左边的项是变量名,用来存放该集合中的每个元素. 该循环 ...
- 用数组指针遍历数组,FOR/FOREACH遍历数组
1. 用数组指针遍历一维数组 <?php header("Content-type:text/html;charset=utf-8"); /*用数组指针遍历一位数组的值*/ ...
- foreach遍历数组
foreach遍历一维数组 <?php //PHP数组遍历:foreach //定义数组 $arr=array(1,2,3,4,5,6,7,8,9,10); //foreach循环 foreac ...
- C# 表达式树遍历(二)
一.前言 上一篇我们对表达式树有了初步的认识,这里我们将对表达式树进行遍历,只有弄清楚了他的运行原理,我们才可以对他进行定制化修改. 表达式系列目录 C# 表达式树讲解(一) C# 表达式树遍历(二) ...
- C#实现在foreach遍历中删除集合中的元素(方法总结)
目录 方法一:采用for循环,并且从尾到头遍历 方法二:使用递归 方法三:通过泛型类实现IEnumerator 在foreach中删除元素时,每一次删除都会导致集合的大小和元素索引值发生变化,从而导致 ...
- 关于for与forEach遍历集合中对集合进行操作的问题
遍历List集合,在循环中再对List集合进行操作,有时候会遇到ConcurrentModificationException(并发修改异常);其实只有在forEach循环集合再对集合操作会发生异常: ...
- 用<forEach>遍历list集合时,提示我找不到对象的属性
<c:forEach items="${list}" var="item"> <tr> <td>${item.UserId} ...
- Foreach遍历
前天在项目中遇到一个问题,foreach遍历过程中修改responses中的对象,其中responses的类型:IEnumerable<Order>,代码如下: foreach (Orde ...
- 使用yield关键字让自定义集合实现foreach遍历
一般来说当我们创建自定义集合的时候为了让其能支持foreach遍历,就只能让其实现IEnumerable接口(可能还要实现IEnumerator接口) 但是我们也可以通过使用yield关键字构建的迭代 ...
随机推荐
- Java编程之字符集问题研究
1. 概述 本文主要包括以下几个方面:编码基本知识,java,系统软件,url,工具软件等. 在下面的描述中,将以"中文"两个字为例,经查表可以知道其GB2312编码是" ...
- MBProgressHUD的基本使用
MBProgressHUD的基本使用 分类: IOS2012-10-30 11:19 12047人阅读 评论(2) 收藏 举报 和gitHub上的Demo其实差不多,就是小整理了下,当备忘,想做复杂的 ...
- MySQL执行外部sql脚本
1:-/mysql_test/test.sql create table student( sno int not null primary key auto_increment, sname ) n ...
- Delphi:窗体自适应屏幕分辨率(根据预设值的比例改变)
delphi 程序适应屏幕分辨率,先在表单单元的Interface部分定义两个常量, 表示设计时的屏幕的宽度和高度(以像素为单位). 在表单的Create事件中先判断 当前分辨率是否与设计分辨率相同, ...
- WPF自定义控件与样式(15)-终结篇
原文:WPF自定义控件与样式(15)-终结篇 系列文章目录 WPF自定义控件与样式(1)-矢量字体图标(iconfont) WPF自定义控件与样式(2)-自定义按钮FButton WPF自定义控件与 ...
- PullToRefreshListView 内嵌checkbox 数据丢失问题
在PullToRefreshListView 内部内嵌了Checkbox如下图所示: 原本设计思路是:对CheckBox 进行 setOnCheckedChangeListener 监听 当Check ...
- Linux系统编程(35)—— socket编程之TCP服务器的并发处理
我们知道,服务器通常是要同时服务多个客户端的,如果我们运行上一篇实现的server和client之后,再开一个终端运行client试试,新的client就不能能得到服务了.因为服务器之支持一个连接. ...
- Java并发3-多线程面试题
1) 什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速. 2) 线程和进程有什 ...
- Ubuntu中apt-get出现E:Encountered a section with no Package: header……的解决方案
方法一:运行命令apt-get update更新list列表 方法二:将/var/lib/apt/lists/下的所有list文件都删除,然后再update
- ORACLE归档模式和非归档模式的利与弊
转: 在Oracle数据库中,主要有两种日志操作模式,分别为非归档模式与归档模式.默认情况下,数据库采用的是非归档模式.作为一个合格的数据库管理员,应当深入了解这两种日志操作模式的特点,并且在数据库建 ...