您的位置:首页 > 其它

实现迭代器的捷径

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

迭代模式是通过IEnumerator和IEnumerable接口及它们的泛型等价物来封装的。
foreach语句被编译后会调用GetEnumerator和MoveNext方法以及Current属性,如果实现IDisposable,程序还是自动销毁迭代对象。
C#1:手写迭代器痛苦
迭代模式不用一次返回所有数据——调用代码一次只需获取一个元素。

#region6-1使用新集合类型代码

object[]values={"a","b","c","d","e"};

IterationSamplecollection=newIterationSample(values,3);

foreach(objectxincollection)

{

Console.WriteLine(x);

}

#endregion


#region新集合类型框架,不包含迭代器的实现

publicclassIterationSample:IEnumerable

{

publicobject[]values;

publicintstartingPoint;


publicIterationSample(object[]values,intstartingPoint)

{

this.values=values;

this.startingPoint=startingPoint;

}


publicIEnumeratorGetEnumerator()

{

returnnewIterationSampleIterator(this);

}


}

#endregion


#region6-3嵌套类实现集合迭代器

classIterationSampleIterator:IEnumerator

{

IterationSampleparent;//正在迭代的集合

intposition;


internalIterationSampleIterator(IterationSampleparent)

{

this.parent=parent;

position=-1;

}


publicboolMoveNext()//将枚举数推进到集合的下一个元素。

{

if(position!=parent.values.Length)//遍历,增加position值

{

position++;

}

returnposition<parent.values.Length;

}


publicobjectCurrent//获取集合中的当前元素。先调用MoveNext方法

{

get

{

if(position==-1||position==parent.values.Length)//防止访问第一个元素之前和最后一个元素之后

{

thrownewInvalidOperationException();

}

intindex=position+parent.startingPoint;

index=index%parent.values.Length;//获取当前索引下标

returnparent.values[index];

}

}


publicvoidReset()

{

position=-1;

}

}

#endregion


C#2:利用yield语句简化迭代器

#region6-4利用C#2和yieidreturn来迭代实例

publicIEnumeratorGetEnumerator()

{

for(intindex=0;index<values.Length;index++)

{

yieldreturnvalues[(index+startingPoint)%values.Length];//实现迭代器的方法

}

}

#endregion


迭代器工作流程

#region6-5

staticreadonlystringPadding=newstring('',30);

staticIEnumerable<int>CreateEnumerable()

{

Console.WriteLine("{0}StartofCreateEnumerable()",Padding);


for(inti=0;i<3;i++)

{

Console.WriteLine("{0}Abouttoyield{1}",Padding,i);

yieldreturni;//代码停止执行,下次调用MoveNext时继续调用

Console.WriteLine("{0}Afteryield",Padding);

}

Console.WriteLine("{0}Yieldingfinalvalue",Padding);

yieldreturn-1;


Console.WriteLine("{0}EndofCreateEnumerable()",Padding);//返回false结束方法执行

}

#endregion


#region6-5显示迭代器及其调用者之间的调用序列

IEnumerable<int>iterable=CreateEnumerable();

IEnumerator<int>iterator=iterable.GetEnumerator();

Console.WriteLine("stringtoiterate");

//while(true)

//{

//Console.WriteLine("CallingMoveNext()...");

//boolresult=iterator.MoveNext();

//Console.WriteLine("...MoveNextresult={0}",iterator.Current);

//}

for(inti=0;i<7;i++)

{

Console.WriteLine("CallingMoveNext()...");

boolresult=iterator.MoveNext();

Console.WriteLine("...MoveNextresult={0}",result);

}

#endregion


return作用:

一:给调用者提供返回值
二:退出时执行合适的finally代码块
使用yieldbreak结束迭代器的执行,finally代码块的执行

#region6-6演示yieldbreak语句


DateTimestop=DateTime.Now.AddSeconds(1);//停止时间

Stopwatchstopwatch=newStopwatch();

stopwatch.Start();//测试时间

foreach(intiinCountWithTimeLimit(stop))

{

Console.WriteLine("Received{0}",i);


Thread.Sleep(10);//执行一次,间隔时间

}

stopwatch.Stop();

doublemm=stopwatch.ElapsedMilliseconds;

Console.WriteLine(mm);

#endregion


#region6-6

staticIEnumerable<int>CountWithTimeLimit(DateTimelimit)

{

try

{

for(inti=1;i<=100;i++)

{

if(DateTime.Now>=limit)

{

yieldbreak;//时间到了,停止运行

}

yieldreturni;//暂时停止方法,并没有退出方法

}

}

finally

{

Console.WriteLine("Stooping");

}

}

#endregion


具体实现中的奇特之处

第一次调用MoveNext之前,Current属性总是返回迭代器产生类型的默认值
在MoveNext返回false之后,Current属性总是返回最后的生成值
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: