Page 70 - MSDN Magazine, April 2017
P. 70

you need to clean up the state after it exits the loop (because either all iterations have completed or an exception is thrown). To achieve this, the IEnumerator<T> interface derives from IDisposable. Enumerators that implement IEnumerator don’t necessarily imple­ ment IDisposable, but if they do, Dispose will be called, as well. This enables the calling of Dispose after the foreach loop exits. The C# equivalent of the final CIL code, therefore, looks like Figure 3.
Notice that because the IDisposable interface is supported by IEnumerator<T>, the using statement can simplify the code in Figure 3 to what is shown in Figure 4.
However, recall that the CIL doesn’t directly support the using keyword. Thus, the code in Figure 3 is actually a more accurate C# representation of the foreach CIL code.
foreach without IEnumerable: C# doesn’t require that IEnumerable/IEnumerable<T> be implemented to iterate over a data type using foreach. Rather, the compiler uses a concept known as duck typing; it looks for a GetEnumerator method that returns a type with a Current property and a MoveNext method. Duck typing involves searching by name rather than relying on an interface
Figure 5 Iterator Interfaces Pattern
or explicit method call to the method. (The name “duck typing” comes from the whimsical idea that to be treated as a duck, the object must merely implement a Quack method; it need not implement an IDuck interface.) If duck typing fails to find a suitable implementation of the enumerable pattern, the compiler checks whether the collection implements the interfaces.
Introducing Iterators
Now that you understand the internals of the foreach implemen­ tation, it’s time to discuss how iterators are used to create custom implementations of the IEnumerator<T>, IEnumerable<T> and corresponding nongeneric interfaces for custom collections. Iterators provide clean syntax for specifying how to iterate over data in collection classes, especially using the foreach loop, allowing the end users of a collection to navigate its internal structure with­ out knowledge of that structure.
The problem with the enumeration pattern is that it can be cumbersome to implement manually because it must maintain all the state necessary to describe the current position in the collec­ tion. This internal state might be simple for a list collection type class; the index of the current position suffices. In contrast, for data structures that require recursive traversal, such as binary trees, the
Figure 6 Yielding Some C# Keywords Sequentially
using System;
using System.Collections.Generic;
public class BinaryTree<T>: IEnumerable<T>
{
public BinaryTree ( T value) {
Value = value; }
#region IEnumerable<T>
public IEnumerator<T> GetEnumerator() {
// ... }
#endregion IEnumerable<T>
public T Value { get; } // C# 6.0 Getter-only Autoproperty
public Pair<BinaryTree<T>> SubItems { get; set; } }
public struct Pair<T>: IEnumerable<T> {
public Pair(T first, T second) : this() {
First = first;
Second = second; }
public T First { get; } public T Second { get; }
#region IEnumerable<T>
public IEnumerator<T> GetEnumerator() {
yield return First;
yield return Second; }
#endregion IEnumerable<T>
#region IEnumerable Members System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator() {
return GetEnumerator(); }
#endregion
// ... }
using System;
using System.Collections.Generic;
public class CSharpBuiltInTypes: IEnumerable<string> {
public IEnumerator<string> GetEnumerator() {
yield return "object"; yield return "byte"; yield return "uint"; yield return "ulong"; yield return "float"; yield return "char"; yield return "bool"; yield return "ushort"; yield return "decimal"; yield return "int"; yield return "sbyte"; yield return "short"; yield return "long"; yield return "void"; yield return "double"; yield return "string";
}
// because IEnumerable<T> derives from IEnumerable. System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator() {
// Invoke IEnumerator<string> GetEnumerator() above.
return GetEnumerator(); }
}
public class Program {
static void Main() {
var keywords = new CSharpBuiltInTypes(); foreach (string keyword in keywords)
{
Console.WriteLine(keyword); }
} }
// The IEnumerable.GetEnumerator method is also required
56 msdn magazine
Essential .NET










































   68   69   70   71   72