实现迭代器的捷径

迭代器模式是行为模式的一种范例,行为模式是一种简化对象之间通信的设计模式。它允许你访问一个数据序列中的所有元素,而无须关心序列是什么类型。它能有效的构建一个数据管道,经过一系列不同的转换或过滤后再从管道的另一端出来。(也是LINQ核心模式之一)

迭代模式是通过IEnumerator和IEnumerable接口及它们的泛型等价物来封装的。
foreach语句被编译后会调用GetEnumerator和MoveNext方法以及Current属性,如果实现IDisposable,程序还是自动销毁迭代对象。
C#1:手写迭代器痛苦
迭代模式不用一次返回所有数据——调用代码一次只需获取一个元素。
#region 6-1使用新集合类型代码
object[] values = { "a", "b", "c", "d", "e" };
IterationSample collection = new IterationSample(values, 3);
foreach (object x in collection)
{
Console.WriteLine(x);
}
#endregion
#region 新集合类型框架,不包含迭代器的实现
public class IterationSample : IEnumerable
{
public object[] values;
public int startingPoint;
 
public IterationSample(object[] values, int startingPoint)
{
this.values = values;
this.startingPoint = startingPoint;
}
 
public IEnumerator GetEnumerator()
{
return new IterationSampleIterator(this);
}
 
}
#endregion
#region 6-3 嵌套类实现集合迭代器
class IterationSampleIterator : IEnumerator
{
IterationSample parent;//正在迭代的集合
int position;
 
internal IterationSampleIterator(IterationSample parent)
{
this.parent = parent;
position = -1;
}
 
public bool MoveNext()// 将枚举数推进到集合的下一个元素。
{
if (position != parent.values.Length)//遍历,增加position值
{
position++;
}
return position < parent.values.Length;
}
 
public object Current// 获取集合中的当前元素。先调用MoveNext方法
{
get
{
if (position == -1 || position == parent.values.Length)//防止访问第一个元素之前和最后一个元素之后
{
throw new InvalidOperationException();
}
int index = position + parent.startingPoint;
index = index % parent.values.Length;//获取当前索引下标
return parent.values[index];
}
}
 
public void Reset()
{
position = -1;
}
}
#endregion
C#2:利用yield语句简化迭代器
#region 6-4 利用C#2和yieid return来迭代实例
public IEnumerator GetEnumerator()
{
for (int index = 0; index < values.Length; index++)
{
yield return values[(index + startingPoint) % values.Length];//实现迭代器的方法
}
}
#endregion
迭代器工作流程
#region 6-5
static readonly string Padding = new string(' ', 30);
static IEnumerable<int> CreateEnumerable()
{
Console.WriteLine("{0}Start of CreateEnumerable()", Padding);
 
for (int i = 0; i < 3; i++)
{
Console.WriteLine("{0}About to yield {1}", Padding, i);
yield return i; //代码停止执行,下次调用MoveNext时继续调用
Console.WriteLine("{0}After yield", Padding);
}
Console.WriteLine("{0}Yielding final value", Padding);
yield return -1;
 
Console.WriteLine("{0} End of CreateEnumerable()", Padding); //返回false结束方法执行
}
#endregion
#region 6-5显示迭代器及其调用者之间的调用序列
IEnumerable<int> iterable = CreateEnumerable();
IEnumerator<int> iterator = iterable.GetEnumerator();
Console.WriteLine("string to iterate");
//while (true)
//{
// Console.WriteLine("Calling MoveNext()...");
// bool result = iterator.MoveNext();
// Console.WriteLine("...MoveNext result={0}", iterator.Current);
//}
for (int i = 0; i < 7; i++)
{
Console.WriteLine("Calling MoveNext()...");
bool result = iterator.MoveNext();
Console.WriteLine("...MoveNext result={0}", result);
}
#endregion
return作用:
一:给调用者提供返回值
二:退出时执行合适的finally代码块
使用yield break结束迭代器的执行,finally代码块的执行
#region 6-6演示yield break语句
 
DateTime stop = DateTime.Now.AddSeconds(1);//停止时间
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();//测试时间
foreach (int i in CountWithTimeLimit(stop))
{
Console.WriteLine("Received {0}", i);
 
Thread.Sleep(10);//执行一次,间隔时间
}
stopwatch.Stop();
double mm = stopwatch.ElapsedMilliseconds;
Console.WriteLine(mm);
#endregion
#region 6-6
static IEnumerable<int> CountWithTimeLimit(DateTime limit)
{
try
{
for (int i = 1; i <= 100; i++)
{
if (DateTime.Now >= limit)
{
yield break;//时间到了,停止运行
}
yield return i;//暂时停止方法,并没有退出方法
}
}
finally
{
Console.WriteLine("Stooping");
}
}
#endregion
具体实现中的奇特之处
第一次调用MoveNext之前,Current属性总是返回迭代器产生类型的默认值
在MoveNext返回false之后,Current属性总是返回最后的生成值

posted on 2016-01-29 15:45  Mr.Tan&  阅读(227)  评论(0编辑  收藏  举报

导航