C # 2.0 specification (iterator) (ii)

Source: Internet
Author: User
Tags foreach execution expression finally block goto stmt variable microsoft c
22.4 Yield statement
The yield statement is used for an 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 is of special significance only before the return or break keyword. In other contexts, it can be used as an identifier.

There are several limitations to where the yield statement can appear, as described below.

L yield statements that occur outside the method body, the Operation Fu, and the accessor body result in a compile-time error.

L yield statements that occur within an anonymous method result in a compile-time error.

A compile-time error occurs when the L yield statement appears in the finally statement of the try statement.

The L yield return statement will cause 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; 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
Yield break; Ok
}
catch {
Yield return 4; Error, yield return in Try...catch
Yield break; Ok
}

D d = Delegate {
Yield return 5; Error, yield in anonymous method
};
}

int MyMethod () {
Yield return 1; Error, error return type of iterator block
}



An implicit conversion (§6.1) must exist from the expression type in the yield return statement to the generation type (§22.1.3) of the iterator.

The yield return statement executes as follows.

L The expression given in the statement is computed (evaluate), implicitly converted to the generating type, and assigned to the current property of the enumerator object.

The execution of the L 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 L enumerator object returns true to the caller, indicating that the enumerator object successfully advances to the next item.



The next call to the MoveNext method of the enumerator object begins execution again from where the iterator block hangs.

The Yeld break statement executes as follows.

L IF the yield break statement is contained in one or more try blocks with a finally block, the initial control is transferred to the finally block of the innermost try statement. When 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 CONTROLS the caller returning to the iterator block. This may be due to an enumerator object's MoveNext method or Dispose method.



The end point of the yield break statement can never be reached because the yield break statement unconditionally transfers control elsewhere.



22.4.1 definitely assigned value
For yield return statements in the form of yield return expr stmt



L like stmt, the opening variable V of expr has a definite assignment state.

L if V is definitely assigned at the end point of expr, it will be definitely assigned at the end point of the stmt, otherwise the stmt end 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 elements in the stack that are from the top to the bottom.

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 to 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.;
Goto __loop;
__state2:
__state = 2;
return false;
}





public void Dispose () {
__state = 2;
}

void Ienumerator.reset () {
throw new NotSupportedException ();
}
}
In previous transformations, 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 the 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, 10);
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 the new __enumerable1 (from, to);
}





Class __enumerable1:
Ienumerable<int>, IEnumerable,
Ienumerator<int> a 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 invoked, the enumerable object itself is returned. A GetEnumerator call to a subsequent enumerable object, if any, that returns a copy of the enumerable object. Therefore, each returned enumerator has its own state, and changing an enumerator will not affect the other. The Interlocked.compareexchange method is used to ensure thread-safe operation.



The From and to parameters are converted to fields of enumerable classes. Since from is modified within the iterator block, a second __from field is introduced to hold the initial value of from in each enumeration.

This method throws a InvalidOperationException exception if __state is called 0 o'clock MoveNext. This will prevent the occurrence of an enumerable object being used as an enumerator, without the first call to GetEnumerator.

(C # 2.0 specification full text)



Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.