迭代器模式提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示
对于一个集合类,必然涉及到添加删除操作,同时也需要支持遍历元素的操作,虽然我们也可以把遍历对象的操作也放在集合类中,但这就违背了单一职责原则,我们要尽可能地分离这些职责,用不同的类去承担不同的职责,迭代器模式就是将存储数据和遍历数据的责任进行了分离
二:适用场景
——访问一个聚合对象中的各个元素
三:迭代器模式的实现
迭代器模式的实现包含四个部分
——抽象迭代器Iterator:定义了访问和遍历元素的接口,需要在具体迭代器类中实现这些方法
——具体迭代器TestIterator:实现抽象迭代器接口,记录遍历中的当前位置
——抽象聚合类IteratorAggregate:创建相应的迭代器对象的接口。带有一个GetIterator()方法用于创建迭代器对象
——具体聚合类Test:实现集合类并实现GetIterator()方法
using System;
using System.Collections;
using System.Collections.Generic;
class MainClass
{
static void Main()
{
int[] numArray = { 1, 2, 3, 4, 5 };
Test test = new Test(numArray);
Iterator it = test.GetIterator();
while (it.CanNext())
{
Console.WriteLine(it.CurObj);
it.Next();
}
}
}
public class Test : IteratorAggregate
{
public int[] numArray;
public Test(int[] numArray)
{
this.numArray = numArray;
}
public Iterator GetIterator()
{
return new TestIterator(this);
}
}
public class TestIterator : Iterator
{
Test test;
int index;
public object CurObj
{
get
{
if (index < test.numArray.Length)
{
return test.numArray[index];
}
throw new InvalidOperationException();
}
}
public TestIterator(Test test)
{
this.test = test;
index = 0;
}
public bool CanNext()
{
if (index < test.numArray.Length)
{
return true;
}
return false;
}
public void Next()
{
if (index < test.numArray.Length)
{
index++;
}
}
public void Reset()
{
index = 0;
test = null;
}
}
public interface Iterator
{
object CurObj { get; }
bool CanNext();
void Next();
void Reset();
}
public interface IteratorAggregate
{
Iterator GetIterator();
}
执行顺序
1.调用对象的GetIterator接口,创建迭代器
2.循环判断是否还有元素,直到返回值为false
3.输出元素
4.得到下一个元素下标
四:.NET中的迭代器
在.NET中,抽象聚合类和抽象迭代器已经被定义好,分别是IEnumerable和IEnumerator接口
通过上面的迭代器模式的实现可知,一个类之所以能够被foreach迭代,是因为实现了IEnumerable接口
现在我们就可以很好的理解foreach内部执行原理了,foreach其实只是一个语法糖,编译器内部是使用while循环来实现的
.NET中自定义迭代器:
using System;
using System.Collections;
class MainClass
{
static void Main(string[] args)
{
int[] numArray = { 1, 2, 3, 4, 5 };
Test test = new Test(numArray);
foreach (var temp in test)
{
Console.WriteLine(temp);
}
}
}
public class Test : IEnumerable
{
public int[] numArray;
public Test(int[] numArray)
{
this.numArray = numArray;
}
public IEnumerator GetEnumerator()
{
return new MyEnumerator(this);
}
}
public class MyEnumerator : IEnumerator
{
Test curObj;
int index;
public MyEnumerator(Test curObj)
{
this.curObj = curObj;
index = -1;
}
public object Current
{
get
{
if (index == -1 && index == curObj.numArray.Length)
{
throw new InvalidOperationException();
}
return curObj.numArray[index];
}
}
public bool MoveNext()
{
if (index <= curObj.numArray.Length)
{
index++;
}
return index < curObj.numArray.Length;
}
public void Reset()
{
index = -1;
}
}
.NET中迭代器的内部执行顺序:
1.首先初始化状态
2.调用MoveNext得到下一个数据的下标
3.调用Current属性得到返回的值
4.当MoveNext方法返回false时迭代结束
目前.NET提供了一种更简便的方法:yield return语句,大大的减少了代码量,yield return告诉编译器这不是一个普通方法而是一个需要执行的迭代块,执行时编译器内部会为我们自动创建并实现了上面IEnumerator接口类
using System;
using System.Collections;
class MainClass
{
static void Main(string[] args)
{
int[] numArray = { 1, 2, 3, 4, 5 };
Test test = new Test(numArray);
foreach (var temp in test)
{
Console.WriteLine(temp);
}
}
}
public class Test : IEnumerable
{
public int[] numArray;
public Test(int[] numArray)
{
this.numArray = numArray;
}
public IEnumerator GetEnumerator()
{
for (int i = 0; i < numArray.Length; i++)
{
yield return numArray[i];
}
}
}
五:迭代器的延迟计算特性
由迭代器的内部执行顺序可知,当调用MoveNext方法时迭代器才会执行,所以迭代器是延迟计算的
using System;
using System.Collections;
using System.Collections.Generic;
class MainClass
{
static void Main(string[] args)
{
WithIterator();
Console.WriteLine("=====");
WithNoIterator();
Console.WriteLine("=====");
foreach (var item in WithIterator())
{
Console.WriteLine("使用foreach输出,当前i的值为:" + item);
}
}
public static IEnumerable WithNoIterator()
{
List list = new List();
for (int i = 0; i < 5; i++)
{
Console.WriteLine("当前i的值为:{0}", i);
list.Add(i);
}
return list;
}
public static IEnumerable WithIterator()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine("当前i的值为:{0}", i);
yield return i;
}
}
}
六:其他
例如我们要获取到集合中是否有元素,通常使用Count(),但使用Count()获得集合元素数量必然要迭代完所有元素,时间复杂度为O(n),而使用MoveNext只需要迭代一次就可以,时间复杂度为O(1),Linq为我们提供了扩展方法:Any()
bool b=list.Count() > 0; bool b=list.GetEnumerator().MoveNext(); bool b = list.Any();



