实现迭代器的捷径
迭代器模式是行为模式的一种范例,行为模式是一种简化对象之间通信的设计模式。它允许你访问一个数据序列中的所有元素,而无须关心序列是什么类型。它能有效的构建一个数据管道,经过一系列不同的转换或过滤后再从管道的另一端出来。(也是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; } } #endregionC#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); } #endregionreturn作用:
一:给调用者提供返回值
二:退出时执行合适的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属性总是返回最后的生成值
如本文对您有帮助请移步右下角,推荐本文,谢谢大家的点赞,因为您的支持是我最大动力
浙公网安备 33010602011771号