You’ve seen how you can use the C# foreach statement to conveniently iterate over a collection of objects, including a System.Array, ArrayList, List<T>, and so on. How does this work? The answer is that each collection that expects to work with foreach must implement the IEnumerable<T> or IEnumerable interface that foreach uses to obtain an object that knows how to enumerate, or iterate over, the items in the collection. The iterator object obtained from IEnumerable<T> must implement the IEnumerator<T> or IEnumerator interface. Generic collection types typically implement IEnumerable<T>, and the enumerator object implements IEnumerator<T>. IEnumerable<T> derives from IEnumerable, and IEnumerator<T> derives from IEnumerator. This allows you to use generic collections in places where nongeneric collections are used. Strictly speaking, your collection types are not required to implement enumerators, and users can iterate through the collection using a for loop if you provide an index operator by implementing IList<T>, for example. However, you won’t make many friends that way, and once I show you how easy it is to create enumerators using iterator blocks, you’ll see that it’s a piece of cake to implement IEnumerable<T> and IEnumerator<T>.

Many of you may already be familiar with the nongeneric enumerator interfaces and how to implement enumerators on your collection types. In the rest of this section, I’ll quickly go over the salient points of creating enumerators from scratch, and I’ll quickly transition to how to create enumerators the new and improved way using iterator blocks. If you’d like, you may skip to the next section on iterators. Or if you want a refresher on implementing enumerators, go ahead and read the rest of this section.

The IEnumerable<T> interface exists so that clients have a well-defined way to obtain an enumerator on the collection. The following code defines the IEnumerable<T> and IEnumerable interfaces:
<pre class="brush:csharp">
public interface IEnumerable<T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}

public interface IEnumerable
{
IEnumerator GetEnumerator();
}
</pre>
Since both interfaces implement GetEnumerator with the same overload signature (remember, the return value doesn’t take part in overload resolution), any collection that implements IEnumerable<T> needs to implement one of the GetEnumerator methods explicitly. It makes the most sense to implement the non-generic IEnumerable.GetEnumerator method explicitly to make the compiler happy.

The IEnumerator<T> and IEnumerator interfaces are shown here:
<pre class="brush:csharp">
public interface IEnumerator<T> : IEnumerator, IDisposable
{
T Current { get; }
}

public interface IEnumerator
{
object Current { get; }
bool MoveNext();
void Reset();
}
</pre>
Again, the two interfaces implement a member that has the same signature, which, in this case, is the Current property. When implementing IEnumerator<T>, you should implement IEnumerator.Current explicitly. Also, notice that IEnumerator<T> implements the IDisposable interface. Later, I’ll explain why this is a good thing.

Now I’m going to show you how to implement IEnumerable<T> and IEnumerator<T> for a homegrown collection type. Good teachers always show you how to do something the "hard way" before introducing you to the "easy way." I think this technique is useful because it forces you to understand what is happening under the covers. When you know what’s happening underneath, you’re more adept at dealing with the technicalities that may come from using the "easy way." Let’s look at an example of implementing IEnumerable<T> and IEnumerator<T> the hard way by introducing a home-grown collection of integers. I’ll show how to implement the generic versions, because that implies that you must also implement the nongeneric versions as well. In this example, I haven’t implemented ICollection<T> so as not to clutter the example, because I’m focusing on the enumeration interfaces:
<pre class="brush:csharp">
using System;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
public class MyColl<T> : IEnumerable<T>
{
public MyColl( T[] items ) {
this.items = items;
}

public IEnumerator<T> GetEnumerator() {
return new NestedEnumerator( this );
}

IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}

// The enumerator definition.
class NestedEnumerator : IEnumerator<T>
{
public NestedEnumerator( MyColl<T> coll ) {
Monitor.Enter( coll.items.SyncRoot );
this.index = -1;
this.coll = coll;
}
public T Current {
get { return current; }
}
object IEnumerator.Current {
get { return Current; }
}
public bool MoveNext() {
if( ++index >= coll.items.Length ) {
return false;
} else {
current = coll.items[index];
return true;
}
}

public void Reset() {
current = default(T);
index = 0;
}

public void Dispose() {
try {
current = default(T);
index = coll.items.Length;
}
finally {
Monitor.Exit( coll.items.SyncRoot );
}
}
private MyColl<T> coll;
private T current;
private int index;
}
private T[] items;
}

public class EntryPoint
{
static void Main() {
MyColl<int> integers =
new MyColl<int>( new int[] {1, 2, 3, 4} );
foreach( int n in integers ) {
Console.WriteLine( n );
}
}
}
</pre>
This example initializes the internal array within MyColl<T> with a canned set of integers, so that the
enumerator will have some data to play with. Of course, a real container should implement ICollection<T> to allow you to populate the items in the collection dynamically. The foreach statements expand into code that obtains an enumerator by calling the GetEnumerator method on the IEnumerable<T> interface. The compiler is smart enough to use IEnumerator<T>.GetEnumerator rather than IEnumerator.GetEnumerator in this case. Once it gets the enumerator, it starts a loop, where it first calls MoveNext and then initializes the variable n with the value returned from Current. If the loop contains no other exit paths, the loop will continue until MoveNext returns false. At that point, the enumerator finishes enumerating the collection, and you must call Reset on the enumerator in order to use it again.

Even though you could create and use an enumerator explicitly, I recommend that you use the foreach construct instead. You have less code to write, which means fewer opportunities to introduce inadvertent bugs. Of course, you might have good reasons to manipulate the enumerators directly. For example, your enumerator could implement special methods specific to your concrete enumerator type that you need to call while enumerating collections. If you must manipulate an enumerator directly, be sure to always do it inside a using block, because IEnumerator<T> implements IDisposable.

Notice that there is no synchronization built into enumerators by default. Therefore, one thread could enumerate over a collection, while another thread modifies it. If the collection is modified while an enumerator is referencing it, the enumerator is semantically invalid, and subsequent use could produce undefined behavior. If you must preserve integrity within such situations, then you may want your enumerator to lock the collection via the object provided by the SyncRoot property. The obvious place to obtain the lock would be in the constructor for the enumerator. However, you must also release the lock at some point. You already know that in order to provide such deterministic cleanup, you must implement the IDisposable interface. That’s exactly one reason why IEnumerator<T> implements the IDisposable interface. Moreover, the code generated by a foreach statement creates a try/finally block under the covers that calls Dispose on the enumerator within the finally block. You can see the technique in action in my previous example.


Source Of Information : Apress Accelerated C Sharp 2010

0 comments


Subscribe to Developer Techno ?
Enter your email address:

Delivered by FeedBurner