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关键字构建的迭代 ...
随机推荐
- ValidateCode.cs验证码时设置缓存的使用
using System; using System.Collections.Generic; using System.Web; using System.Web.UI; using System. ...
- css3 animation 动画属性简介
animation 动画属性介绍 animation 属性是一个简写属性,用于设置动画属性: 1. animation-name----规定需要绑定到选择器的 keyframe 名称. 语法:anim ...
- JQUERY1.9学习笔记 之基本过滤器(六) 页眉选择器
页眉选择器jQuery( ":header" ) 描述:选择页眉的所有标签,如 h1,h2, h3 等. <!DOCTYPE html><html lang=&q ...
- PHPCMS v9 在windows2008系统 IIS7 下设置伪静态的方法
安装环境:windows2008+IIS7.0+PHP5+MYSQL5 一.安装phpcms v9程序,设置伪静态.如图: 二.安装IIS7官方Url重写模块 1.先到IIS官方下载模块 下载地址:h ...
- PHP判断远程图片或文件是否存在
PHP判断远程图片是否存在,此方法同样适用于判断远程文件是否存在,这是一种既然有效率且又准确的方法,建议采用此方法,以往使用get_headers()方法判断都是有问题的: function chec ...
- Linux启动网卡时出现RTNETLINK answers: File exists错误解决方法
这里说一下,如果复制了虚拟机,设置新的MAC地址为什么? 在虚拟机的网络设置中--->高级.然后找到如下窗口,生成新的MAC地址即可. ----------------------------- ...
- jdk8永久代从方法区移除的验证
/*** 测试使用jdk8中是否仍然可以使用永久代* jvm options * -Xms20m -Xmx20m -Xmn10m -XX:PermSize=10m -XX:MaxPermSize=10 ...
- file_get_content、fsockopen和curl之间的优缺点
file_get_content 优点:在抓取单个文件上,效率很高,返回没有头信息的文件. 缺点:在抓取远程文件时,和fopen一样容易出错.在抓取多个跨域文件时,未对DNS进行缓存,所以效率上不不高 ...
- c++ primer( 文本查询程序)
读取用户指定的任意文本文件,然后允许用户从该文件查找单词,查询的结果是该单词出现的次数,并列出每次出现所在的行,如果某单词在同一行中多次出现,程序将只显示改行的一次.行号按升序显示(int main( ...
- PowerShell控制台输出符号+函数参数类型指定+文本内容读取
There are several ways: Write-Host: Write directly to the console, not included in function/cmdlet o ...