Page 68 - MSDN Magazine, June 2017
P. 68

the code are executed. However, you can also use yield break to cause MoveNext to return false and control to return immediately to the caller and end the loop. Here’s an example of such a method:
public System.Collections.Generic.IEnumerable<T> GetNotNullEnumerator()
{
}
This method cancels the iteration if either of the elements in the Pair<T> class is null.
Figure 4 Placing Yield Return Statements Within a Loop
A yield break statement is similar to placing a return statement at the top of a function when it’s determined there’s no work to do. It’s a way to exit from further iterations without surrounding all remaining code with an if block. As such, it allows multiple exits. Use it with caution, because a casual reading of the code might overlook the early exit.
How Iterators Work
When the C# compiler encounters an iterator, it expands the code into the appropriate CIL for the corresponding enumerator design pattern. In the generated code, the C# compiler first creates a nested private class to implement the IEnumerator<T> interface, along with its Current property and a MoveNext method. The Current
Figure 6 C# Equivalent of Compiler-Generated C# Code for Iterators
if((First == null) || (Second == null)) {
yield break;
}
yield return Second; yield return First;
public class BinaryTree<T>: IEnumerable<T> {
// ...
#region IEnumerable<T>
public IEnumerator<T> GetEnumerator() {
// Return the item at this node. yield return Value;
// Iterate through each of the elements in the pair. foreach (BinaryTree<T> tree in SubItems)
{
if (tree != null) {
// Because each element in the pair is a tree, // traverse the tree and yield each element. foreach (T item in tree)
{
yield return item; }
} }
}
#endregion IEnumerable<T>
#region IEnumerable Members System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator() {
return GetEnumerator(); }
#endregion }
using System;
using System.Collections.Generic;
public class Pair<T> : IPair<T>, IEnumerable<T> {
// ...
// The iterator is expanded into the following // code by the compiler.
public virtual IEnumerator<T> GetEnumerator() {
__ListEnumerator result = new __ListEnumerator(0); result._Pair = this;
return result;
}
public virtual System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator() {
return new GetEnumerator(); }
private sealed class __ListEnumerator<T> : IEnumerator<T> {
public __ListEnumerator(int itemCount) {
_ItemCount = itemCount; }
Pair<T> _Pair; T _Current; int _ItemCount;
public object Current {
get {
return _Current; }
}
public bool MoveNext() {
switch (_ItemCount) {
case 0:
_Current = _Pair.First; _ItemCount++;
return true;
case 1:
_Current = _Pair.Second; _ItemCount++;
return true;
default: return false;
} }
} }
Figure 5 Using foreach with BinaryTree<string>
// JFK
var jfkFamilyTree = new BinaryTree<string>(
"John Fitzgerald Kennedy");
jfkFamilyTree.SubItems = new Pair<BinaryTree<string>>( new BinaryTree<string>("Joseph Patrick Kennedy"), new BinaryTree<string>("Rose Elizabeth Fitzgerald"));
// Grandparents (Father's side) jfkFamilyTree.SubItems.First.SubItems =
new Pair<BinaryTree<string>>(
new BinaryTree<string>("Patrick Joseph Kennedy"), new BinaryTree<string>("Mary Augusta Hickey"));
// Grandparents (Mother's side) jfkFamilyTree.SubItems.Second.SubItems =
new Pair<BinaryTree<string>>(
new BinaryTree<string>("John Francis Fitzgerald"), new BinaryTree<string>("Mary Josephine Hannon"));
foreach (string name in jfkFamilyTree) {
Console.WriteLine(name); }
64 msdn magazine
Essential .NET





















   66   67   68   69   70