C# 2.0 Specification(迭代器)(二)

22.4 yield 语句

yield 语句用于迭代器块以产生一个枚举器对象值,或表明迭代的结束。

embedded-statement: (嵌入语句)
...
yield-statement ( yield 语句)

yield-statement: ( yield 语句)
yield return expression ;
yield break ;

为了确保和现存程序的兼容性, yield 并不是一个保留字,并且 yield 只有在紧邻 return 或 break 关键词之前才具有特别的意义。而在其他上下文中,它可以被用作标识符。

yield 语句所能出现的地方有几个限制,如下所述。

l yield 语句出现在方法体、运算符体和访问器体之外时,将导致编译时错误。

l yield 语句出现在匿名方法之内时,将导致编译时错误。

l yield 语句出现在 try 语句的 finally 语句中时,将导致编译时错误。

l yield return 语句出现在包含 catch 子语句的任何 try 语句中任何位置时,将导致编译时错误。

如下示例展示了 yield 语句的一些有效和无效用法。

delegate IEnumerable

  1<int> D(); 
  2
  3IEnumerator<int> GetEnumerator() {   
  4try {   
  5yield return 1;  // Ok   
  6yield break;  // Ok   
  7}   
  8finally {   
  9yield return 2;  //  错误  , yield  在  finally  中    
 10yield break;  //  错误  , yield  在  finally  中    
 11} 
 12
 13  
 14
 15
 16try {   
 17yield return 3;  //  错误  , yield return  在  try...catch  中    
 18yield break;  // Ok   
 19}   
 20catch {   
 21yield return 4;  //  错误  , yield return  在  try...catch  中    
 22yield break;  // Ok   
 23} 
 24
 25D d = delegate {   
 26yield return 5;  //  错误  , yield  在匿名方法中    
 27};   
 28} 
 29
 30int MyMethod() {   
 31yield return 1;  //  错误  ,  迭代器块的错误返回类型    
 32} 
 33
 34从  yield return  语句中表达式类型到迭代器的产生类型  (§  22.1.3  )  ,必须存在隐式转换  (§6.1)  。 
 35
 36yield return  语句按如下方式执行。 
 37
 38l  在语句中给出的表达式将被计算(  evaluate  ),隐式地转换到产生类型,并被赋给枚举器对象的  Current  属性。 
 39
 40l  迭代器块的执行将被挂起。如果  yield return  语句在一个或多个  try  块中,与之关联的  finally  块此时将不会执行。 
 41
 42l  枚举器对象的  MoveNext  方法对调用方返回  true  ,表明枚举器对象成功前进到下一个项。 
 43
 44对枚举器对象的  MoveNext  方法的下一次调用,重新从迭代器块挂起的地方开始执行。 
 45
 46yeld break  语句按如下方式执行。 
 47
 48l  如果  yield break  语句被包含在一个或多个带有  finally  块的  try  块内,初始控制权将转移到最里面的  try  语句的  finally  块。当控制到达  finally  块的结束点后,控制将会转移到下一个最近的  try  语句的  finally  块。这个过程将会一直重复直到所有内部的  try  语句的  finally  块都被执行。 
 49
 50l  控制返回到迭代器块的调用方。这可能是由于枚举器对象的  MoveNext  方法或  Dispose  方法。 
 51
 52由于  yield break  语句无条件的转移控制到别处,所以  yield break  语句的结束点将永远不能到达。 
 53
 54**   
 55**
 56
 57###  22.4.1  明确赋值 
 58
 59对于以  yield return expr  形式的  yield return  语句  stmt 
 60
 61l  像  stmt  开始一样,在  expr  的开头变量  v  具有明确的赋值状态。 
 62
 63l  如果在  expr  的结束点  v  被明确赋值,那它在  stmt  的结束点也将被明确赋值;否则,在  stmt  结束点将不会被明确赋值。 
 64
 65##  22.5  实现例子 
 66
 67本节以标准  C#  构件的形式描述了迭代器的可能实现。此处描述的实现基于与  Microsoft C#  编译器相同的原则,但这绝不是强制或唯一可能的实现。 
 68
 69如下  Stack<t> 类使用迭代器实现了  GetEnumerator  方法。该迭代器依序枚举了堆栈中从顶到底的元素。 
 70
 71using System;   
 72using System.Collections;   
 73using System.Collections.Generic; 
 74
 75class Stack<t>: IEnumerable<t>   
 76{   
 77T[] items;   
 78int count; 
 79
 80public void Push(T item) {   
 81if (items == null) {   
 82items = new T[4];   
 83}   
 84else if (items.Length == count) {   
 85T[] newItems = new T[count * 2];   
 86Array.Copy(items, 0, newItems, 0, count);   
 87items = newItems;   
 88}   
 89items[count++] = item;   
 90} 
 91
 92public T Pop() {   
 93T result = items[--count];   
 94items[count] = T.default;   
 95return result;   
 96} 
 97
 98public IEnumerator<t> GetEnumerator() {   
 99for (int i = count - 1; i &gt;= 0; --i) yield items[i];   
100}   
101} 
102
103  
104
105
106GetEnumerator  方法可以被转换到编译器生成的枚举器类的实例,该类封装了迭代器块中的代码,如下所示。 
107
108class Stack<t>: IEnumerable<t>   
109{   
110... 
111
112public IEnumerator<t> GetEnumerator() {   
113return new __Enumerator1(this);   
114} 
115
116class __Enumerator1: IEnumerator<t>, IEnumerator   
117{   
118int __state;   
119T __current;   
120Stack<t> __this;   
121int i; 
122
123public __Enumerator1(Stack<t> __this) {   
124this.__this = __this;   
125} 
126
127public T Current {   
128get { return __current; }   
129} 
130
131object IEnumerator.Current {   
132get { return __current; }   
133} 
134
135public bool MoveNext() {   
136switch (__state) {   
137case 1: goto __state1;   
138case 2: goto __state2;   
139}   
140i = __this.count - 1;   
141__loop:   
142if (i &lt; 0) goto __state2;   
143__current = __this.items[i];   
144__state = 1;   
145return true;   
146__state1:   
147\--i;   
148goto __loop;   
149__state2:   
150__state = 2;   
151return false;   
152} 
153
154  
155
156
157public void Dispose() {   
158__state = 2;   
159} 
160
161void IEnumerator.Reset() {   
162throw new NotSupportedException();   
163}   
164}   
165在先前的转换中,迭代器块之内的代码被转换成  state machine  ,并被放置在枚举器类的  MoveNext  方法中。此外局部变量  i  被转换成枚举器对象的一个字段,因此在  MoveNext  的调用过程中可以持续存在。 
166
167下面的例子打印一个简单的从整数  1  到  10  的乘法表。该例子中  FromTo  方法返回一个可枚举对象,并且使用迭代器实现。 
168
169using System;   
170using System.Collections.Generic; 
171
172class Test   
173{   
174static IEnumerable<int> FromTo(int from, int to) {   
175while (from &lt;= to) yield return from++;   
176} 
177
178static void  Main  () {   
179IEnumerable<int> e = FromTo(1, 10);   
180foreach (int x in e) {   
181foreach (int y in e) {   
182Console.Write("{0,3} ", x * y);   
183}   
184Console.WriteLine();   
185}   
186}   
187} 
188
189FromTo  方法可被转换成编译器生成的可枚举类的实例,该类封装了迭代器块中的代码,如下所示。 
190
191using System;   
192using System.Threading;   
193using System.Collections;   
194using System.Collections.Generic; 
195
196class Test   
197{   
198... 
199
200static IEnumerable<int> FromTo(int from, int to) {   
201return new __Enumerable1(from, to);   
202} 
203
204  
205
206
207class __Enumerable1:   
208IEnumerable<int>, IEnumerable,   
209IEnumerator<int>, IEnumerator   
210{   
211int __state;   
212int __current;   
213int __from;   
214int from;   
215int to;   
216int i; 
217
218public __Enumerable1(int __from, int to) {   
219this.__from = __from;   
220this.to = to;   
221} 
222
223public IEnumerator<int> GetEnumerator() {   
224__Enumerable1 result = this;   
225if (Interlocked.CompareExchange(ref __state, 1, 0) != 0) {   
226result = new __Enumerable1(__from, to);   
227result.__state = 1;   
228}   
229result.from = result.__from;   
230return result;   
231} 
232
233IEnumerator IEnumerable.GetEnumerator() {   
234return (IEnumerator)GetEnumerator();   
235} 
236
237public int Current {   
238get { return __current; }   
239} 
240
241object IEnumerator.Current {   
242get { return __current; }   
243} 
244
245public bool MoveNext() {   
246switch (__state) {   
247case 1:   
248if (from &gt; to) goto case 2;   
249__current = from++;   
250__state = 1;   
251return true;   
252case 2:   
253__state = 2;   
254return false;   
255default:   
256throw new InvalidOperationException();   
257}   
258} 
259
260public void Dispose() {   
261__state = 2;   
262} 
263
264void IEnumerator.Reset() {   
265throw new NotSupportedException();   
266}   
267}   
268} 
269
270这个可枚举类实现了可枚举接口和枚举器接口,这使得它成为可枚举的或枚举器。当  GetEnumerator  方法被首次调用时,将返回可枚举对象自身。后续可枚举对象的  GetEnumerator  调用,如果有的话,都返回可枚举对象的拷贝。因此,每次返回的枚举器都有其自身的状态,改变一个枚举器将不会影响另一个。  Interlocked.CompareExchange  方法用于确保线程安全操作。 
271
272from  和  to  参数被转换为可枚举类的字段。由于  from  在迭代器块内被修改,所以引入另一个  __from  字段来保存在每个枚举其中  from  的初始值。 
273
274如果当  __state  是  0  时  MoveNext  被调用,该方法将抛出  InvalidOperationException  异常。这将防止没有首次调用  GetEnumerator  ,而将可枚举对象作为枚举器而使用的  现象发生。 
275
276(C# 2.0 Specification 全文完) 
277
278吁....&amp;^%终于敲完,该歇歇了,呵呵,眼睛冒火花**&amp;&amp;^%%...</int></int></int></int></int></int></t></t></t></t></t></t></t></t></t></t></int></int>
Published At
Categories with Web编程
Tagged with
comments powered by Disqus