C # 2.0 specification (iterator) (2)

zhaozj2021-02-16  51

22.4 Yield sentence

The Yield statement is used for iterator blocks to generate an enumerator object value, or indicate the end of 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 is only particularly significant before close to Return or Break keywords. In other context, it can be used as an identifier.

There are several constraints that the Yield statement can appear, as described below.

The L Yield statement appears outside the method, the operator, and the access device, will cause errors when compiling.

l Yield statement occurs within an anonymous method, will cause errors when compiling.

l Yield statement When you have a Finally statement of the TRY statement, it will cause errors when compiling.

The L Yield Return statement occurs anywhere in any of the TRY statements containing the CATCH spectrient, will result in compile time errors.

Some valid and invalid uses of Yield statements are shown as follows.

Delegate IEnumerable d ();

IEnumerator getenumerator () {yield return 1; // ok yield break; // ok} finally {yield return 2; // error, yield in finally Yield Break; // error, yield in Finally}

Try {yield return 3; // Error, Yield Return in Try ... catch Eld break; // ok} catch {yield return 4; // error, Yield Return In Try ... catch; OK}

D D = delegate {yield return 5; // error, Yield in anonymous method};

INT mymethod () {yield return 1; // error, error return type of iterator block}

From the Yield Return statement, the expression type to the generated type of iterator (§22.1.3), there must be implicit conversion (§6.1).

The Yield RetURN statement is executed as follows.

l The expression given in the statement will be calculated, implicitly converted to the generating type, and is assigned the CURRENT property of the enumerator object.

l 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.

l The MoveNext method of the envelope object returns true to the caller, indicating that the enumerator object is successfully advanced to the next item.

The next call of the MoveNext method of the enumerator object is resended from the place where the iterator block hangs.

The YELD BREAK statement is executed as follows.

l If the Yield Break statement is included in a TRY block with a Finally block, the initial control will be transferred to the Finally block of the most inside the TRY statement. When controlling the end point of the FINALLY block, the control will be transferred to the next nearest TRY Finally block. This process will be repeated until all the Finally blocks of all internal TRY statements are executed. l Control returns the caller of the iterator block. This may be due to the MoveNext method of the enumerator object or the Dispose method.

Since the Yield Break statement is unconditional transfer control, the endpoint of the Yield Break statement will never arrive.

22.4.1 Clear assignment

For Yield Return statement STMT in the form of Yield Return EXPR

L Like Stmt, the beginning variable V at EXPR has a clear assignment state at the beginning variable V.

l If the end point V is explicitly assigned at the EXPR, it will also be explicitly assigned at the end point of the STMT; otherwise, the end point at the STMT will not be explicitly assigned.

22.5 Implementation example

This section describes the possible implementation of the iterator in the form of a standard C # component. The implementation described herein is based on the same principles as the Microsoft C # compiler, but this is by no means for mandatory or unique possible implementation.

The following STACK class uses an iterator to implement the GetEnumerator method. The iterator sequentially enumerates the elements from the top to the bottom in the stack.

Using system.collections; usneric;

Class Stack : IEnumerable {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; returnrate;}

Public IEnumerator getENUMERATOR () {for (int i = count - 1; i> = 0; --I) Yield items [i];}}

The GetENUMERATOR method can be converted to an instance of an enumerator class generated by the compiler, which encapsulates the code in the iterator block, as shown below.

Class Stack : ienumerable {...

Public IEnumerator getenumerator () {return new __enumerator1 (this);}

Class __enumerator1: ienumerator , ienumerator {int __state; t __current; stack __THIS; INT i;

Public __enumerator1 (stack __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; goto __loop; __state2: __State = 2; return false;}

Public void dispose () {__State = 2;

Void IEnumerator.reset () {throw new notSupportedException ();}} The code within the iterator block is converted to State Machine and is placed in the MoveNext method of the enumerator class. Furthermore, the local variable i is converted into a field of an enumerator object, so it can last in the calling process 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.collections.Generic;

Class test {static {{while (from <= to) Yield Return from ;}

Static void

Main

() {IEnumerable E = fromto (1, 10); Foreach (int x in e) {finch (int y in e) {console.write ("{0,3}", x * y); Console.writeLine ();}}}

The FROMTO method can be converted into an example of an enumerable class generated by the compiler, which encapsulates the code in the iterator block, as shown below.

Using system.chreading; using system.collections; usneric;

Class test {...

static IEnumerable FromTo (int from, int to) {return new __Enumerable1 (from, to);} class __Enumerable1: IEnumerable , IEnumerable, IEnumerator , IEnumerator {int __state; int __current; int __from; Int from; int in;

Public __enumerable11 (int __from, int to) {this .__ from = __from; this.to = to;}

public IEnumerator GetEnumerator () {__Enumerable1 result = this; if (! Interlocked.CompareExchange (ref __state, 1, 0) = 0) {result = new (__ from, to) __Enumerable1; result .__ state = 1;} result .from = result .__ from; returnrate;}

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 enumeration class implements an enumerated interface and an enumerator interface, which makes it an enumerable or enumerator. When the GetEnumerator method is first called, it will return to an enumerated object itself. Subsequent GetEnumerator calls, if any, all returns a copy of the enumerable object. Therefore, each returned enumerator has its own state, changing an enumerator will not affect the other. Interlocked.CompareExchange method is used to ensure a thread security operation. The from and the to parameters are converted to the enumerable field. Since the FROM is modified within the iterator block, another __FROM field is introduced to save the initial value of the FROM in each enumerated.

If__state is 0, MoveNext is called, the method will throw an InvalidOperationException exception. This will prevent the GetENUMERATOR that is not first called, and the object can be enumerated as an enumerator.

(C # 2.0 Specification full text)

Calling ... & ^% finally finished, should rest, huh, huh, eyes splashing ** && ^ %% ...

转载请注明原文地址:https://www.9cbs.com/read-25892.html

New Post(0)