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

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             object[] values = new object[] { "a", "b", "c", "d", "e" };
 6             IterationSample sample = new IterationSample(values, 3);
 7             foreach (var item in sample)
 8             {
 9                 Console.WriteLine(item);
10             }
11             Console.ReadKey();
12         }
13     }
14     public class IterationSample : IEnumerable
15     {
16         public object[] values;
17         int startPoint;
18         public IterationSample(object[] values, int startingPoint)
19         {
20             this.values = values;
21             this.startPoint = startingPoint;
22         }
23         public IEnumerator GetEnumerator()
24         {
25             //return new IterationSampleIterator(this);
26             for (int index = 0; index < values.Length; index++)
27             {
28                 yield return values[(index + startPoint) % values.Length];
29             } 
30         }
31     }
32     public class IterationSampleIterator : IEnumerator
33     {
34         IterationSample parent;
35         int position;
36         public IterationSampleIterator(IterationSample parent)
37         {
38             this.parent = parent;
39             this.position = -1;
40         }
41         public object Current
42         {
43             get
44             {
45                 if (position == -1 || position == parent.values.Length)
46                 {
47                     throw new InvalidOperationException();
48                 }
49                 int index = position + parent.values.Length;
50                 index = index % parent.values.Length;
51                 return parent.values[index];
52             }
53         }
54 
55         public bool MoveNext()
56         {
57             if (position != parent.values.Length)
58             {
59                 position++;
60             }
61             return position < parent.values.Length;
62         }
63 
64         public void Reset()
65         {
66             position = -1;
67         }
68     }

6.2.2 观察迭代器的工作流程

 1     class Program
 2     {
 3         static readonly string Padding = new string(' ', 30);
 4         static void Main(string[] args)
 5         {
 6             IEnumerable<int> iterable = CreatteEnumerable(Padding);
 7             IEnumerator<int> iterator = iterable.GetEnumerator();
 8             Console.WriteLine("starting iterate");
 9 
10             while (true)
11             {
12                 Console.WriteLine("=======================================");
13                 Console.WriteLine("calling MoveNext()");
14                 bool result = iterator.MoveNext();
15                 Console.WriteLine("moveNext result = {0}", result);
16                 if (!result)
17                     break;
18                 Console.WriteLine("fetching current");
19                 Console.WriteLine("current result = {0}", iterator.Current);
20             }
21 
22             Console.ReadKey();
23         }
24         static IEnumerable<int> CreatteEnumerable(string Padding)
25         {
26             Console.WriteLine("{0} start of createEnumerbale padding", Padding);
27 
28             for (int i = 0; i < 3; i++)
29             {
30                 Console.WriteLine("{0} about to yield {1}", Padding, i);
31                 yield return i;
32                 Console.WriteLine("{0} after padding", Padding);
33             }
34             Console.WriteLine("{0} yield final value ", Padding);
35             yield return -1;
36             Console.WriteLine("{0} end of createEnumerable();", Padding);
37         }
38         /*
39         
40         starting iterate
41         =======================================
42         calling MoveNext()
43                                        start of createEnumerbale padding
44                                        about to yield 0
45         moveNext result = True
46         fetching current
47         current result = 0
48         =======================================
49         calling MoveNext()
50                                        after padding
51                                        about to yield 1
52         moveNext result = True
53         fetching current
54         current result = 1
55         =======================================
56         calling MoveNext()
57                                        after padding
58                                        about to yield 2
59         moveNext result = True
60         fetching current
61         current result = 2
62         =======================================
63         calling MoveNext()
64                                        after padding
65                                        yield final value
66         moveNext result = True
67         fetching current
68         current result = -1
69         =======================================
70         calling MoveNext()
71                                        end of createEnumerable();
72         moveNext result = False
73 
74          */
75     }

 6.2.3 进一步了解迭代器执行流程

1. 使用 yield break 结束迭代器的执行

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             DateTime stop = DateTime.Now.AddSeconds(2);
 6             foreach (var item in CountWithTimeLimit(stop))
 7             {
 8                 Console.WriteLine("received {0}", item);
 9                 Thread.Sleep(300);
10             }
11             Console.ReadKey();
12         }
13         static IEnumerable<int> CountWithTimeLimit(DateTime limit)
14         {
15             for (int i = 0; i < 100; i++)
16             {
17                 if (DateTime.Now >= limit)
18                 {
19                     yield break;
20                 }
21                 yield return i;
22             }
23         }
24     }

2.  finally 代码块的执行

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             DateTime stop = DateTime.Now.AddSeconds(2);
 6             foreach (var item in CountWithTimeLimit(stop))
 7             {
 8                 Console.WriteLine("received {0}", item);
 9                 if (item > 3)
10                 {
11                     Console.WriteLine("returning");
12                     return;
13                 }
14                 Thread.Sleep(300);
15             }
16             Console.ReadKey();
17         }
18         static IEnumerable<int> CountWithTimeLimit(DateTime limit)
19         {
20             try
21             {
22                 for (int i = 0; i < 100; i++)
23                 {
24                     if (DateTime.Now >= limit)
25                     {
26                         yield break;
27                     }
28                     yield return i;
29                 }
30             }
31             finally
32             {
33                 Console.WriteLine("stopping");
34                 Console.ReadKey();
35             }
36         }
37         /*
38         received 0
39         received 1
40         received 2
41         received 3
42         received 4
43         returning
44         stopping
45          */
46     }

foreach 会在它自己的 finally 代码块中调用 IEnumerator 所提供的Dispose 方法(就像 using 语句)。
当迭代器完成迭代之前,你如果调用由迭代器代码块创建的迭代器上的 Dispose ,
那么状态机就会执行在代码当前“暂停”位置范围内的任何 finally 代码块。
这个解释复杂且有点详细,但结果却很容易描述:只要调用者使用了 foreach 循环,迭代器块中的 finally 将按照你期望的方式工作。

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             DateTime stop = DateTime.Now.AddSeconds(2);
 6             IEnumerable<int> iterable = CountWithTimeLimit(stop);
 7             IEnumerator<int> iterator = iterable.GetEnumerator();
 8 
 9             iterator.MoveNext();
10             Console.WriteLine("received {0}", iterator.Current);
11 
12             iterator.MoveNext();
13             Console.WriteLine("received {0}", iterator.Current);
14 
15             Console.ReadKey();
16         }
17         static IEnumerable<int> CountWithTimeLimit(DateTime limit)
18         {
19             try
20             {
21                 for (int i = 0; i < 100; i++)
22                 {
23                     if (DateTime.Now >= limit)
24                     {
25                         yield break;
26                     }
27                     yield return i;
28                 }
29             }
30             finally
31             {
32                 Console.WriteLine("stopping");
33                 Console.ReadKey();
34             }
35         }
36         /*
37         received 0
38         received 1
39          */
40     }

幸好,作为开发人员我们不需要太关心编译器是如何解决这些问题的。不过,关于实现中的以下一些奇特之处还是值得了解的:
 在第一次调用 MoveNext 之前, Current 属性总是返回迭代器产生类型的默认值;
 在 MoveNext 返回 false 之后, Current 属性总是返回最后的生成值;
 Reset 总是抛出异常,而不像我们手动实现的重置过程那样,为了遵循语言规范,这是必要的行为;
 嵌套类总是实现 IEnumerator 的泛型形式和非泛型形式(提供给泛型和非泛型的IEnumerable 所用)。

6.3.2 迭代文件中的行

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             string fileName = string.Format(@"{0}aaa.txt", AppDomain.CurrentDomain.BaseDirectory);
 6             foreach (var item in ReadLines(fileName))
 7             {
 8                 Console.WriteLine(item);
 9             }
10 
11             Console.ReadKey();
12         }
13         static IEnumerable<string> ReadLines(string fileName)
14         {
15             using (TextReader reader = File.OpenText(fileName))
16             {
17                 string line;
18                 while ((line = reader.ReadLine()) != null)
19                 {
20                     yield return line;
21                 }
22             }
23         }
24     }

 6.3.3 使用迭代器块和谓词对项进行延迟过滤

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             string fileName = string.Format(@"{0}aaa.txt", AppDomain.CurrentDomain.BaseDirectory);
 6 
 7             IEnumerable<string> lines = ReadLines(fileName);
 8             Predicate<string> predicate = line => line.StartsWith("using");
 9 
10             foreach (var item in Where(lines, predicate))
11             {
12                 Console.WriteLine(item);
13             }
14 
15             Console.ReadKey();
16         }
17         public static IEnumerable<T> Where<T>(IEnumerable<T> source, Predicate<T> predicate)
18         {
19             if (source.IsNull() || predicate.IsNull())
20                 throw new ArgumentException();
21 
22             return WhereImpl(source, predicate);
23         }
24         private static IEnumerable<T> WhereImpl<T>(IEnumerable<T> source, Predicate<T> predicate)
25         {
26             foreach (T item in source)
27             {
28                 if (predicate(item))
29                 {
30                     yield return item;
31                 }
32             }
33         }
34         static IEnumerable<string> ReadLines(string fileName)
35         {
36             return ReadLines(() => { return File.OpenText(fileName); });
37         }
38         static IEnumerable<string> ReadLines(Func<TextReader> provider)
39         {
40             using (TextReader reader = provider())
41             {
42                 string line;
43                 while ((line = reader.ReadLine()) != null)
44                 {
45                     yield return line;
46                 }
47             }
48         }
49     }
原文地址:https://www.cnblogs.com/kikyoqiang/p/10015675.html