迭代器的实现
迭代器的实现
迭代器模式是行为模式的一种范例,行为模式是一种简化对象之间通信的一种设计模式。在.NET中,迭代器模式是通过IEnumerator和IEnumerable接口以及它们的泛型版本来实现的。如果某个类实现了IEnumerable接口,就说明它可以被迭代访问,调用GetEnumerator()方法将返回IEnumerator的实现,这个就是迭代器本身。
在C# 1.0中,利用foreach语句实现了访问迭代器的内置支持,让集合的遍历变得简单、明了。其实,foreach的实现就是调用GetEnumerator和MoveNext方法以及Current属性。所以说,在C# 1.0中要获得迭代器就必须实现IEnumerable接口中的GetEnumerator方法,要实现一个迭代器就要实现IEnumerator接口中的MoveNext和Reset方法。
//定义一个类,获得迭代器,实现IEnumerable接口
public class MyIEnumerable : IEnumerable
{
private string[] strList;
public MyIEnumerable(string[] strList)
{
this.strList = strList;
}
public IEnumerator GetEnumerator()
{
//C#1.0返回一个实现的迭代器
return new MyIEnumerator(strList);
}
}
//实现迭代器,实现IEnumerator接口中的MoveNext和Reset方法
public class MyIEnumerator : IEnumerator
{
private string[] strList;
private int position;
public MyIEnumerator(string[] strList)
{
this.strList = strList;
position = -1;
}
public object Current
{
get
{
return strList[position];
}
}
public bool MoveNext()
{
position++;
if (position < strList.Length)
return true;
return false;
}
public void Reset()
{
position = -1;
}
}
上面这段代码,要40行代码,在C# 2.0中提供的语法糖来简化迭代器的实现,可以通过yield关键字来简化迭代器的实现。下列类中的GetEnumerator()方法它不是一个普通的方法,这个yield return告诉c#编译器,这是实现一个迭代器的方法。这个方法被声明为返回一个IEnumerator接口,所以就只能使用迭代器块来实现返回类型为IEnumerable、IEnumerator或泛型等价物的方法。在迭代器块中不允许包含普通的return语句——只能是yield return。
//定义一个类,获得迭代器,实现IEnumerable接口
public class MyIEnumerable : IEnumerable
{
private string[] strList;
public MyIEnumerable(string[] strList)
{
this.strList = strList;
}
public IEnumerator GetEnumerator()
{
//使用c#2.0中yield关键字来实现,不需要定义MyIEnumerator类就能实现
for(int i = 0; i<strList.Length;i++)
{
yield return strList[i];
}
}
}
也可以创建一个类来实现这个迭代器。使用“c#嵌套类型可以访问它外层类型的私有类型”这一特点,就是说,我们仅需要存储一个指向“父级”MyIEnumerable类型的引用和关于所访问到的位置的状态,如下所示。注意,我们能看到MyIEnumerator这个类是MyIEnumerable的子类。
public class MyIEnumerable : IEnumerable
{
private object[] values;
private int startPosition;
public MyIEnumerable(object[] values, int startPosition=0)
{
this.values = values;
this.startPosition = startPosition;
}
public IEnumerator GetEnumerator()
{
//return new MyIEnumerator(this);
for(int i=0;i<values.Length;i++)
{
yield return values[(i + startPosition) % values.Length];
}
}
public class MyIEnumerator : IEnumerator
{
private MyIEnumerable parent;
private int position;
internal MyIEnumerator(MyIEnumerable parent)
{
this.parent = parent;
position = -1;
}
public object Current
{
get
{
if (position == -1)
throw (new System.InvalidOperationException("枚举尚未开始,请先调用MoveNext"));
return parent.values[(position + parent.startPosition) % parent.values.Length];
}
}
public bool MoveNext()
{
if (position != parent.values.Length)
position++;
return position < parent.values.Length;
}
public void Reset()
{
position = -1;
}
}
}
接下来我们创建一个控制台程序来运行它,这里我用了迭代的方式,这两种运行结果是一样的,可以看出,这个foreach语句中实际就是运行了这个迭代器,先使用GetEnumerator方法之后实现迭代器,然后使用MoveNext语句。不过这两种还是有一个不同的,foreach会在最后调用Dispose方法,而且这很重要。还有一个小知识,foreach语句中的那个集合实际上实现了GetEnumerator方法的都可以运行,而不是想象中的必须实现IEnumerable接口。
static void Main(string[] args)
{
string[] strList = new string[] { "a", "b", "c" };
MyIEnumerable mi = new MyIEnumerable(strList);
foreach(var item in mi)
{
Console.WriteLine(item);
}
Console.WriteLine("==========================================================");
var list = mi.GetEnumerator();
while(list.MoveNext())
{
Console.WriteLine(list.Current);
}
Console.ReadKey();
}
还有如果你运行下列代码你会发现有趣的东西,运行结果为“b,c,a”。这是我看《深入了解c#》看到的,就是,这个自己实现的迭代器,可以设置它的逻辑“起点”。所以可以从1开始,就能看到元素1,2和0依次返回。
static void Main(string[] args)
{
string[] strList = new string[] { "a", "b", "c" };
MyIEnumerable mi = new MyIEnumerable(strList,1);
foreach(var item in mi)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
本文主要借鉴了《深入理解c#》,非常感谢。
下一篇: # touch.js和zepto.js
推荐阅读
-
判断php数组是否为索引数组的实现方法_PHP教程
-
任务管理器消失不见了怎么办 任务管理器找回的两种解决方法
-
jQuery截取指定长度字符串的实现原理及代码_jquery
-
Javascript获取CSS伪元素属性的实现代码_javascript技巧
-
编写一个函数reverse_string(char * string)(递归实现)实现:将参数字符串中的字符反向排列。 要求:不能使用C函数库中的字符串操作函数
-
php升级 - 正在运行中的服务器,如何平滑从php5.2升级到php5.3 ,linux+php-fpm+nginx
-
JVM入门之类加载与字节码技术(类加载与类的加载器)
-
在服务器的mysql中创建自定义函数出错
-
Vue实现Element UI的下拉框默认选中,值来自父组件或异步获取
-
Java基于swing实现的弹球游戏代码