Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C-sharp language specification.2004.pdf
Скачиваний:
14
Добавлен:
23.08.2013
Размер:
2.55 Mб
Скачать

Chapter 27 Iterators

1enumerator object that encapsulates the code specified in the iterator block, and execution of the code in the

2iterator block occurs when the enumerator object’s MoveNext method is invoked. An enumerable object has

3the following characteristics:

4It implements IEnumerable and IEnumerable<T>, where T is the yield type of the iterator block.

5It is initialized with a copy of the argument values (if any) and instance value passed to the function

6member.

7An enumerable object is typically an instance of a compiler-generated enumerable class that encapsulates the

8code in the iterator block and implements the enumerable interfaces, but other methods of implementation

9are possible. If an enumerable class is generated by the compiler, that class shall be nested, directly or

10indirectly, in the class containing the function member, it shall have private accessibility, and it shall have a

11name reserved for compiler use (§9.4.2).

12[Note: An enumerable object can implement more interfaces than those specified above. In particular, an

13enumerable object can also implement IEnumerator and IEnumerator<T>, enabling it to serve as both

14an enumerable and an enumerator. In that type of implementation, the first time an enumerable object’s

15GetEnumerator method is invoked, the enumerable object itself is returned. Subsequent invocations of the

16enumerable object’s GetEnumerator, if any, return a copy of the enumerable object. Thus, each returned

17enumerator has its own state, and changes in one enumerator will not affect another. end note]

1827.3.1 The GetEnumerator method

19An enumerable object provides an implementation of the GetEnumerator methods of the IEnumerable

20and IEnumerable<T> interfaces. The two GetEnumerator methods share a common implementation that

21acquires and returns an available enumerator object. The enumerator object is initialized with the argument

22values and instance value saved when the enumerable object was initialized, but otherwise the enumerator

23object functions as described in §27.2.

2427.4 Implementation example

25[Note: This section describes a possible implementation of iterators in terms of standard C# constructs. The

26implementation described here is by no means a mandated implementation or the only one possible.

27The following Stack<T> class implements its GetEnumerator method using an iterator. The iterator

28enumerates the elements of the stack in top to bottom order.

29using System;

30using System.Collections;

31using System.Collections.Generic;

32class Stack<T>: IEnumerable<T>

33{

34

T[] items;

35

int count;

36

public void Push(T item) {

37

if (items == null) {

38

items = new T[4];

39

}

40

else if (items.Length == count) {

41

T[] newItems = new T[count * 2];

42

Array.Copy(items, 0, newItems, 0, count);

43

items = newItems;

44

}

45

items[count++] = item;

46

}

47

public T Pop() {

48

T result = items[--count];

49

items[count] = default(T);

50

return result;

51

}

427

 

C# LANGUAGE SPECIFICATION

1

public IEnumerator<T> GetEnumerator() {

2

for (int i = count - 1; i >= 0; --i) yield return items[i];

3}

4}

5The GetEnumerator method can be translated into an instantiation of a compiler-generated enumerator

6class that encapsulates the code in the iterator block, as shown in the following.

7class Stack<T>: IEnumerable<T>

8{

9

10

public IEnumerator<T> GetEnumerator() {

11

return new __Enumerator1(this);

12

}

13

class __Enumerator1: IEnumerator<T>, IEnumerator

14

{

15

int __state;

16

T __current;

17

Stack<T> __this;

18

int i;

19

public __Enumerator1(Stack<T> __this) {

20

this.__this = __this;

21

}

22

public T Current {

23

get { return __current; }

24

}

25

object IEnumerator.Current {

26

get { return __current; }

27

}

28

public bool MoveNext() {

29

switch (__state) {

30

case 1: goto __state1;

31

case 2: goto __state2;

32

}

33

i = __this.count - 1;

34

__loop:

35

if (i < 0) goto __state2;

36

__current = __this.items[i];

37

__state = 1;

38

return true;

39

__state1:

40

--i;

41

goto __loop;

42

__state2:

43

__state = 2;

44

return false;

45

}

46

public void Dispose() {

47

__state = 2;

48

}

49

void IEnumerator.Reset() {

50

throw new NotSupportedException();

51

}

52}

53}

54In the preceding translation, the code in the iterator block is turned into a state machine and placed in the

55MoveNext method of the enumerator class. Furthermore, the local variable i is turned into a field in the

56enumerator object so it can continue to exist across invocations of MoveNext.

57The following example prints a simple multiplication table of the integers 1 through 10. The FromTo

58method in the example returns an enumerable object and is implemented using an iterator.

59using System;

60using System.Collections.Generic;

428

Chapter 27 Iterators

1class Test

2{

3

static IEnumerable<int> FromTo(int from, int to) {

4

while (from <= to) yield return from++;

5

}

6

static void Main() {

7

IEnumerable<int> e = FromTo(1, 10);

8

foreach (int x in e) {

9

foreach (int y in e) {

10

Console.Write("{0,3} ", x * y);

11

}

12

Console.WriteLine();

13

}

14}

15}

16The FromTo method can be translated into an instantiation of a compiler-generated enumerable class that

17encapsulates the code in the iterator block, as shown in the following.

18using System;

19using System.Threading;

20using System.Collections;

21using System.Collections.Generic;

22class Test

23{

24

25

static IEnumerable<int> FromTo(int from, int to) {

26

return new __Enumerable1(from, to);

27

}

28

class __Enumerable1:

29

IEnumerable<int>, IEnumerable,

30

IEnumerator<int>, IEnumerator

31

{

32

int __state;

33

int __current;

34

int __from;

35

int from;

36

int to;

37

int i;

38

public __Enumerable1(int __from, int to) {

39

this.__from = __from;

40

this.to = to;

41

}

42

public IEnumerator<int> GetEnumerator() {

43

__Enumerable1 result = this;

44

if (Interlocked.CompareExchange(ref __state, 1, 0) != 0) {

45

result = new __Enumerable1(__from, to);

46

result.__state = 1;

47

}

48

result.from = result.__from;

49

return result;

50

}

51

IEnumerator IEnumerable.GetEnumerator() {

52

return (IEnumerator)GetEnumerator();

53

}

54

public int Current {

55

get { return __current; }

56

}

57

object IEnumerator.Current {

58

get { return __current; }

59

}

429

 

C# LANGUAGE SPECIFICATION

1

public bool MoveNext() {

2

switch (__state) {

3

case 1:

4

if (from > to) goto case 2;

5

__current = from++;

6

__state = 1;

7

return true;

8

case 2:

9

__state = 2;

10

return false;

11

default:

12

throw new InvalidOperationException();

13

}

14

}

15

public void Dispose() {

16

__state = 2;

17

}

18

void IEnumerator.Reset() {

19

throw new NotSupportedException();

20

}

21}

22}

23The enumerable class implements both the enumerable interfaces and the enumerator interfaces, enabling it

24to serve as both an enumerable and an enumerator. The first time the GetEnumerator method is invoked,

25the enumerable object itself is returned. Subsequent invocations of the enumerable object’s

26GetEnumerator, if any, return a copy of the enumerable object. Thus, each returned enumerator has its

27own state and changes in one enumerator will not affect another. The Interlocked.CompareExchange

28method is used to ensure thread-safe operation.

29The from and to parameters are turned into fields in the enumerable class. Because from is modified in the

30iterator block, an additional __from field is introduced to hold the initial value given to from in each

31enumerator.

32The MoveNext method throws an InvalidOperationException if it is called when __state is 0. This

33protects against use of the enumerable object as an enumerator object without first calling GetEnumerator.

34The following example shows a simple tree class. The Tree<T> class implements its GetEnumerator

35method using an iterator. The iterator enumerates the elements of the tree in infix order.

36using System;

37using System.Collections.Generic;

38class Tree<T>: IEnumerable<T>

39{

40

T value;

41

Tree<T> left;

42

Tree<T> right;

43

public Tree(T value, Tree<T> left, Tree<T> right) {

44

this.value = value;

45

this.left = left;

46

this.right = right;

47

}

48

public IEnumerator<T> GetEnumerator() {

49

if (left != null) foreach (T x in left) yield x;

50

yield value;

51

if (right != null) foreach (T x in right) yield x;

52}

53}

430

Chapter 27 Iterators

1class Program

2{

3

static Tree<T> MakeTree<T>(T[] items, int left, int right) {

4

if (left > right) return null;

5

int i = (left + right) / 2;

6

return new Tree<T>(items[i],

7

MakeTree(items, left, i - 1),

8

MakeTree(items, i + 1, right));

9

}

10

static Tree<T> MakeTree<T>(params T[] items) {

11

return MakeTree(items, 0, items.Length - 1);

12

}

13

// The output of the program is:

14

// 1 2 3 4 5 6 7 8 9

15

// Mon Tue Wed Thu Fri Sat Sun

16

//

17

static void Main() {

18

Tree<int> ints = MakeTree(1, 2, 3, 4, 5, 6, 7, 8, 9);

19

foreach (int i in ints) Console.Write("{0} ", i);

20

Console.WriteLine();

21

Tree<string> strings = MakeTree("Mon", "Tue", "Wed", "Thu",

22

"Fri", "Sat", "Sun");

23

foreach (string s in strings) Console.Write("{0} ", s);

24

Console.WriteLine();

25}

26}

27The GetEnumerator method can be translated into an instantiation of a compiler-generated enumerator

28class that encapsulates the code in the iterator block, as shown in the following.

29class Tree<T>: IEnumerable<T>

30{

31

32

public IEnumerator<T> GetEnumerator() {

33

return new __Enumerator1(this);

34

}

35

sealed class __Enumerator1 : IEnumerator<T>, IEnumerator

36

{

37

Node<T> __this;

38

IEnumerator<T> __left, __right;

39

int __state;

40

T __current;

41

public __Enumerator1(Node<T> __this) { this.__this = __this; }

42

public T Current { get { return __current; } }

43

public bool MoveNext() {

44

try {

45

switch (__state) {

46

case 0:

47

__state = -1;

48

if (__this.left == null) goto __yield_value;

49

__left = __this.left.GetEnumerator();

50

goto case 1;

51

case 1:

52

__state = -2;

53

if (!__left.MoveNext()) goto __left_dispose;

54

__current = __left.Current;

55

__state = 1;

56

return true;

57

__left_dispose:

58

__state = -1;

59

__left.Dispose();

431

 

C# LANGUAGE SPECIFICATION

1

__yield_value:

2

__current = __this.value;

3

__state = 2;

4

return true;

5

case 2:

6

__state = -1;

7

if (__this.right == null) goto __end;

8

__right = __this.right.GetEnumerator();

9

goto case 3;

10

case 3:

11

__state = -3;

12

if (!__right.MoveNext()) goto __right_dispose;

13

__current = __right.Current;

14

__state = 3;

15

return true;

16

__right_dispose:

17

__state = -1;

18

__right.Dispose();

19

__end:

20

__state = 4;

21

break;

22

}

23

} finally {

24

if (__state < 0) Dispose();

25

}

26

return false;

27

}

28

public void Dispose() {

29

try {

30

switch (__state) {

31

case 1:

32

case -2:

33

__left.Dispose();

34

break;

35

case 3:

36

case -3:

37

__right.Dispose();

38

break;

39

}

40

} finally {

41

__state = 4;

42

}

43

}

44

object IEnumerator.Current { get { return Current; } }

45

void IEnumerator.Reset() {

46

throw new NotSupportedException();

47

}

48}

49}

50The compiler generated temporaries used in the foreach statements are lifted into the __left and __right

51fields of the enumerator object. The __state field of the enumerator object is carefully updated so that the

52correct Dispose() method will be called correctly if an exception is thrown. Note that it is not possible to

53write the translated code with simple foreach statements. end note]

432

Соседние файлы в предмете Электротехника