Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Daniel Solis - Illustrated C# 2010 - 2010.pdf
Скачиваний:
16
Добавлен:
11.06.2015
Размер:
11.23 Mб
Скачать

CHAPTER 20 ENUMERATORS AND ITERATORS

Iterators

Enumerable classes and enumerators are used extensively in the .NET collection classes, so it’s important that you know how they work. But now that you know how to create your own enumerable classes and enumerators, you might be pleased to learn that, starting with C# 2.0, the language got a much simpler way of creating enumerators and enumerables. In fact, the compiler will create them for you. The construct that produces them is called an iterator. You can use the enumerators and enumerables generated by iterators wherever you would use manually coded enumerators or enumerables.

Before I explain the details, let’s take a look at two examples. The following method declaration implements an iterator that produces and returns an enumerator.

The iterator returns a generic enumerator that returns three items of type string.

The yield return statements declare that this is the next item in the enumeration.

Return a generic enumerator.

public IEnumerator<string> BlackAndWhite()

// Version 1

{

 

yield return "black"; yield return "gray"; yield return "white";

}

//yield return

//yield return

//yield return

The following method declaration is another version that produces the same result:

Return a generic enumerator.

public IEnumerator<string> BlackAndWhite()

// Version 2

{

 

 

string[]

theColors = { "black", "gray", "white" };

 

for (int

i = 0; i < theColors.Length; i++)

 

yield

return theColors[i];

// yield return

}

 

 

I haven’t explained the yield return statement yet, but on inspecting these code segments, you might have the feeling that something is different about this code. It doesn’t seem quite right. What exactly does the yield return statement do?

For example, in the first version, if the method returns on the first yield return statement, then the last two statements can never be reached. If it doesn’t return on the first statement but continues through to the end of the method, then what happens to the values? And in the second version, if the yield return statement in the body of the loop returns on the first iteration, then the loop will never get to any subsequent iterations.

And besides all that, an enumerator doesn’t just return all the elements in one shot—it returns a new value with each access of the Current property. So, how does this give you an enumerator? Clearly this code is different from anything shown before.

524

CHAPTER 20 ENUMERATORS AND ITERATORS

Iterator Blocks

An iterator block is a code block with one or more yield statements. Any of the following three types of code blocks can be iterator blocks:

A method body

An accessor body

An operator body

Iterator blocks are treated differently than other blocks. Other blocks contain sequences of statements that are treated imperatively. That is, the first statement in the block is executed, followed by the subsequent statements, and eventually control leaves the block.

An iterator block, on the other hand, is not a sequence of imperative commands to be executed at one time. Instead, it’s declarative; it describes the behavior of the enumerator class you want the compiler to build for you. The code in the iterator block describes how to enumerate the elements.

Iterator blocks have two special statements:

The yield return statement specifies the next item in the sequence to return.

The yield break statement specifies that there are no more items in the sequence.

The compiler takes this description of how to enumerate the items and uses it to build an enumerator class, including all the required method and property implementations. The resulting class is nested inside the class where the iterator is declared.

You can have the iterator produce either an enumerator or an enumerable depending on the return type you use for the iterator block, as shown in Figure 20-8.

Figure 20-8. You can have an iterator block produce either an enumerator or an enumerable depending on

the return type you specify.

525

CHAPTER 20 ENUMERATORS AND ITERATORS

Using an Iterator to Create an Enumerator

The following code illustrates how to use an iterator to create an enumerable class.

MyClass uses iterator method BlackAndWhite to produce an enumerator for the class.

MyClass also implements method GetEnumerator, which in turn calls BlackAndWhite, and returns the enumerator that BlackAndWhite returns to it.

Notice that in Main, you can use an instance of the class directly in the foreach statement since the class is enumerable.

class MyClass

 

{

 

 

 

public IEnumerator<string> GetEnumerator()

 

{

 

 

 

return BlackAndWhite();

// Returns the enumerator.

}

 

 

 

Returns an enumerator

 

 

 

 

public IEnumerator<string> BlackAndWhite()

// Iterator

{

 

 

 

yield return "black";

 

yield return "gray";

 

yield return "white";

 

}

 

 

 

}

 

 

 

class Program

 

{

 

 

 

static void Main()

 

{

 

 

 

MyClass mc = new MyClass();

 

 

Use the instance of MyClass.

 

 

 

 

foreach (string shade in mc)

 

Console.WriteLine(shade);

 

}

 

 

 

}

 

 

 

This code produces the following output:

black gray white

526

CHAPTER 20 ENUMERATORS AND ITERATORS

Figure 20-9 shows the code for MyClass on the left and the resulting objects on the right. Notice how much is built for you automatically by the compiler.

The iterator’s code is shown on the left side of the figure and shows that its return type is

IEnumerator<string>.

On the right side of the figure, the diagram shows that the nested class implements

IEnumerator<string>.

Figure 20-9. An iterator block that produces an enumerator

527

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]