22.4 Yield statement
The yield statement is used for the iterator block to produce an enumerator object value, or to indicate the end of the iteration.
Embedded-statement: (embedded statement)
...
Yield-statement (yield statement)
Yield-statement: (yield statement)
yield return expression;
Yield break;
To ensure compatibility with existing programs, yield is not a reserved word, and yield has special meaning only before the return or break keyword is close. In other contexts, it can be used as an identifier.
There are several limitations to where the yield statement can appear, as described below.
The L yield statement occurs outside the method body, the Operation Fu, and the accessor body, resulting in a compile-time error.
The L yield statement will cause a compile-time error when it appears inside an anonymous method.
The L yield statement causes a compile-time error when it appears in the finally statement of the try statement.
The L yield return statement causes a compile-time error when it appears anywhere in any try statement that contains a catch child statement.
The following example shows some valid and invalid usages of the yield statement.
delegate ienumerable<int> D ();ienumerator<int> GetEnumerator () { try {yield return 1;//Okyield break;//ok}finally {yield return 2;//error, yield break in finally;//error, yield in fi Nally in}try {yield return 3;//error, yield return in try...catch yield break;//Ok}catch {yield return 4;//error, yield return Yield break in the try...catch; Ok}d D = delegate {yield return 5;//error, yield in anonymous method}; }int MyMethod () {yield return 1;//error, error return type for iterator block}
An implicit conversion (§6.1) must exist from the expression type in the yield return statement to the generation type of the iterator (§22.1.3).
The yield return statement executes as follows.
The expression given in the statement is evaluated (evaluate), implicitly converted to the resulting type, and assigned to the current property of the enumerator object.
The execution of the iterator block will be suspended. If the yield return statement is in one or more try blocks, the finally block associated with it will not be executed at this time.
The MoveNext method of the enumerator object returns true to the caller, indicating that the enumerator object has successfully advanced to the next item.
The next call to the MoveNext method of the enumerator object is restarted from where the iterator block was suspended.
The Yeld break statement executes as follows.
L IF the yield break statement is contained within one or more try blocks with a finally block, the initial control is transferred to the finally block of the innermost try statement. When the control reaches the end point of the finally block, the control is transferred to the finally block of the next nearest try statement. This process will be repeated until the finally block of all internal try statements is executed.
L control returns to the caller of the iterator block. This may be due to the MoveNext method of the enumerator object or the Dispose method.
Because the yield break statement unconditionally transfers control elsewhere, the end point of the yield break statement will never arrive.
22.4.1 definite Assignment
For yield return statements in the form of yield return expr stmt
L as stmt begins, the variable v at the beginning of expr has a definite assignment state.
L If the end point of expr is definitely assigned, it will also be definitely assigned at the end of the stmt; otherwise, the stmt end point will not be definitely assigned
22.5 implementation Examples
This section describes the possible implementations of iterators in the form of standard C # artifacts. The implementation described here is based on the same principles as the Microsoft C # compiler, but this is by no means a mandatory or only possible implementation.
The following Stack<t> class uses iterators to implement the GetEnumerator method. The iterator sequentially enumerates the top-to-bottom elements of the stack.
Using system;using system.collections;using system.collections.generic;class stack<t>: IEnumerable<T>{T [] items;int count;public void Push (T item) {if (items = null) {items = new t[4];} else if (items. Length = = count) {t[] NewItems = new T[count * 2]; Array.copy (items, 0, NewItems, 0, count); items = NewItems;} items[count++] = Item;} Public T Pop () {t result = items[--count];items[count] = T.default;return result;} Public ienumerator<t> GetEnumerator () {for (int i = count-1; I >= 0; i.) yield items[i];}}
The GetEnumerator method can be converted to an instance of the compiler-generated enumerator class that encapsulates the code in the iterator block, as shown below.
Class Stack<t>: Ienumerable<t>{...public ienumerator<t> GetEnumerator () {return new __Enumerator1 ( this);} Class __enumerator1:ienumerator<t>, Ienumerator{int __state; T __current; Stack<t> __this;int i;public __enumerator1 (stack<t> __this) {this.__this = __this;} Public T-current {get {return __current;}} Object IEnumerator.Current {get {return __current;}} public bool MoveNext () {switch (__state) {case 1:goto __state1;case 2:goto __state2;} i = __this.count-1;__loop:if (i < 0) Goto __state2;__current = __this.items[i];__state = 1;return true;__state1:--i;g Oto __loop;__state2:__state = 2;return false;} public void Dispose () {__state = 2;} void Ienumerator.reset () {throw new NotSupportedException ();}}
In the previous conversion, the code within the iterator block was converted to state machine and placed in the MoveNext method of the enumerator class. In addition, the local variable i is converted to a field of the enumerator object, so it can persist during the invocation of MoveNext.
The following example prints a simple multiplication table from integers 1 to 10. In this example, the FromTo method returns an enumerable object and is implemented using an iterator.
Using system;using system.collections.generic;class test{static ienumerable<int> FromTo (int from, int. to) {while ( from <= to) yield return from++;} static void Main () {ienumerable<int> e = FromTo (1, ten), foreach (int x in e) {foreach (int y in e) {Console.Write ("{0 , 3} ", X * y);} Console.WriteLine ();}}}
The FromTo method can be converted to an instance of the compiler-generated enumerable class that encapsulates the code in the iterator block, as shown below.
Using system;using system.threading;using system.collections;using system.collections.generic;class Test{...static ienumerable<int> FromTo (int from, int to) {return new __enumerable1 (from, to);} Class __enumerable1:ienumerable<int>, Ienumerable,ienumerator<int>, Ienumerator{int __state;int __ Current;int __from;int from;int to;int i;public __enumerable1 (int __from, int to) {This.__from = __from;this.to = to;} Public ienumerator<int> GetEnumerator () {__enumerable1 result = This;if (Interlocked.compareexchange (ref __state , 1, 0)! = 0) {result = new __enumerable1 (__from, to); result.__state = 1;} Result.from = Result.__from;return result;} IEnumerator Ienumerable.getenumerator () {return (IEnumerator) GetEnumerator (); public int Current {get {return __current;}} Object IEnumerator.Current {get {return __current;}} public bool MoveNext () {switch (__state) {case 1:if (from > To) goto case 2;__current = From++;__state = 1;return true; Case 2:__state = 2;return False;defauLt:throw new InvalidOperationException ();}} public void Dispose () {__state = 2;} void Ienumerator.reset () {throw new NotSupportedException ();}}}
This enumerable class implements an enumerable interface and an enumerator interface, which makes it an enumerable or enumerator. When the GetEnumerator method is first called, the enumerable object itself is returned. Subsequent GetEnumerator calls to enumerable objects, if any, return copies of enumerable objects. Therefore, each enumerator returned has its own state, and changing one enumerator will not affect the other. The Interlocked.compareexchange method is used to ensure thread-safe operations.
The From and to parameters are converted to fields of the enumerable class. Since the from is modified within the iterator block, another __from field is introduced to hold the initial value of the from in each enumeration.
If __state is called 0 o'clock MoveNext, the method throws an InvalidOperationException exception. This prevents the phenomenon of using enumerable objects as enumerators without the first call to GetEnumerator.
(C # 2.0 specification Full text end)
The above is the C # 2.0 specification (iterator) (ii) content, more relevant content please pay attention to topic.alibabacloud.com (www.php.cn)!