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关键字构建的迭代 ...
随机推荐
- storm教程
二.安装部署 一.storm伪分布式安装 (一)环境准备1.OS:debian 72.JDK 7.0 (二)安装zookeeper1.下载zookeeper并解压 wget http://mirr ...
- C++ Primer 5th 第9章 顺序容器
练习9.1:对于下面的程序任务,vector.deque和list哪种容器最为适合?解释你的选择的理由.如果没有哪一种容器优于其他容器,也请解释理由.(a) 读取固定数量的单词,将它们按字典序插入到容 ...
- 高级应用与部署 —— 主程序与web目录分离
在网站部署中,考虑网站的安全行问题,可以将您的网站主程序与web目录分离,使主程序在web目录之外,从而提高网站的安全性. 分离方法 1.将phpcms v9中程序主框架目录phpcms移动至web目 ...
- CentOS 添加/绑定 IP
美国VPS的独立IP相对于国内而言,是非常的便宜的.比如有些美国VPS,买5个独立IP才三美元左右一个月.当我们购买了多个独立IP时,如果你不想再联系客服而漫长的等待,那就自己手动配置吧. 一.进入/ ...
- 用Set中元素做条件查询
一个老师教许多学生,一个学生被许多老师教,一个学生有好多书,同一种书被许多同学拥有.查询教拥有书"a"的学生的老师: class teacher{ String id; ...
- 第几天 switch做法 杭电
第几天? Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submi ...
- Android 字体颜色变化(点击)
在开发的过程中,经常会遇到这样的场景,点击按钮,背景颜色发生变化:在drawable中,定义xxx.xml(selector) <selector xmlns:android="htt ...
- Android上传文件之FTP
android客户端实现FTP文件(包括图片)上传应该没什么难度.写下来就了为了记录一下,望能帮到新手. 需要用到 commons-net-3.0.1.jar,后面附上jar包. 直接上代码: /** ...
- qt-solutions提供了8个开源项目
其实这是官方提供的源代码,至于为什么会另建项目,而没有整合到QT项目里去,我猜可能有2个原因: 1. 这几个项目本身不完善,并且也不是QT项目的核心,因此没有必要花精力去完善 2. 一定程度上可以维护 ...
- Java中快速排序的实现
快速排序是对冒泡排序的一种改进.它的基本思想是:通过一躺排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要 小,然后再按次方法对这两部分数据分别进行快速排序,整个排 ...