Generic Enumerators

Nongeneric enumerable objects and enumerators lack type specialty, which leads to performance problems and other issues. You can implement enumerable objects and enumerators using generic interfaces, which avoid some of the problems mentioned at the end of the previous section. Implement the IEnumerable<T> interface for generic enumerable objects. For generic enumerators, implement the IEnumerator<T> interface. Both IEnumerable<T> and IEnumerator<T> are generic interfaces found in the System.Collections.Generic namespace. IEnumerable<T> and IEnumerator<T> inherit their nongeneric counterparts IEnumerable and IEnumerator, respectively. This means the nongeneric methods must be implemented as well.

IEnumerable<T> Interface

Generic enumerable objects implement the IEnumerable<T> interface. This is the IEnumerable<T> interface:

public interface IEnumerable<T> : IEnumerable {
    IEnumerator<T> GetEnumerator();
}

As shown, IEnumerable<T> inherits IEnumerable, which is the nongeneric version of the same interface. This includes a nongeneric version of the GetEnumerator method. Enumerators inheriting IEnumerable<T> therefore must implement both a generic and nongeneric GetEnumerator method. The two GetEnumerator methods differ in return type only. As you know, the return type alone is insufficient for overloading a method. To prevent ambiguity, one of the GetEnumerator methods must use explicit interface member implementation.

This is sample code of the GetEnumerator methods for a generic enumerable object. The nongeneric version of GetEnumerator is implemented explicitly:

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

IEnumerator IEnumerable.GetEnumerator() {
    return new Enumerator<T>(this);
}

The generic version of GetEnumerator naturally returns a generic enumerator, which implements the IEnumerator<T> interface.

IEnumerator<T> Interface

Generic enumerators implement the IEnumerator<T> interface, shown here:

public interface IEnumerator<T>: IDisposable, IEnumerator {
    T Current { get; }
}

Current is a read-only property and the only member of the IEnumerator<T> generic interface. The remaining members are inherited from the IDisposable and IEnumerator interfaces. The IDisposable interface marks generic enumerators as disposable. This requires implementing the IDisposable.Dispose method. The IEnumerator interface adds the nongeneric enumerator interface. The MoveNext and Reset methods do not have an implementation specific to a generic type. Therefore the nongeneric versions are sufficient even for a generic implementation. A second Current property, a nongeneric version, is inherited from the IEnumerable interface. Therefore, IEnumerator has overloaded Current properties, both of which should be implemented in the enumerator.

The following is sample code of a generic and nongeneric implementation of the Current property. The nongeneric Current property simply calls the generic version:

public __T Current {
    get {
        if (oThis.version != version) {
            throw new InvalidOperationException(
                "Collection was modified");
        }

        if (cursor > (oThis.items.Length - 1)) {
            throw new InvalidOperationException(
                "Enumeration already finished");
        }
        if (cursor == -1) {
            throw new InvalidOperationException(
                "Enumeration not started");
        }
        return oThis.items[cursor];
    }
}

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

The Dispose method supports deterministic garbage collection. This method is called explicitly to clean up for an object. In this circumstance, the method is called to clean up resources assigned to an enumerator. Dispose methods of enumerators are most frequently called in the iterators, which is the next topic of this chapter. In the Dispose method, set the state of the enumeration to After and perform any necessary cleanup. Some enumerators track the state using a flag (an enumeration type), where the flag is updated in the Dispose method and elsewhere in the enumerator. The code presented here does not employ a state flag. If the cursor is beyond the collection, the After state is assumed. Conversely, a cursor of –1 indicates the Before state. Based on these assumptions, this is the implementation of a Dispose method in our example for an enumerator:

public void Dispose() {
    cursor = oThis.items.Length + 1;
}

A Generic Enumerator Example (Versioned Collection)

Earlier in this chapter, source code was presented for enumerating a versioned collection. Here is the versioned collection example redone with generic interfaces. The following code also completes some of the partial code presented earlier in this section. In Main, the collection is enumerated in a generic and a nongeneric manner. The first foreach loop uses a generic enumerator. That is the default implementation of an enumerator provided by the SimpleCollection class. In the second foreach loop, the simple collection is cast to the nongeneric IEnumerable interface. This instructs the foreach statement to call the nongeneric GetEnumerator method, which returns a nongeneric enumerator. The nongeneric enumerator is then used to iterate the simple collection.

Here is the sample code:

using System;
using System.Collections;
using System.Collections.Generic;

namespace Donis.CSharpBook {

    public class Starter {

        public static void Main() {
            SimpleCollection<int> simple =
                new SimpleCollection<int>(
                    new int[] {1,2,3,4,5,6,7});

            foreach (int number in simple) {
                Console.WriteLine(number);
            }

            foreach (int number in
                (IEnumerable) simple) {
                Console.WriteLine(number);
            }
        }
    }

    public class SimpleCollection<T>: IEnumerable<T> {

        public SimpleCollection(T[] array) {
            items = array;
            version = 1;
        }

        public T this[int index] {
            get {
                return items[index];
            }
            set {
                ++version;
                items[index] = value;
            }
        }

        public IEnumerator<T> GetEnumerator() {
            Console.WriteLine(
                "IEnumerator<T> GetEnumerator()");
            return new Enumerator<T>(this);
        }

        IEnumerator IEnumerable.GetEnumerator() {
            Console.WriteLine(
                "IEnumerator GetEnumerator()");
            return new Enumerator<T>(this);
        }

        private class Enumerator<__T>: IEnumerator<__T>

        {

            public Enumerator(SimpleCollection<__T> obj) {
                 oThis = obj;
                 cursor = -1;
                 version = oThis.version;
            }

            public __T Current {
                get {
                    if (oThis.version != version) {
                        throw new InvalidOperationException(
                            "Collection was modified");
                    }

                    if (cursor > (oThis.items.Length - 1)) {
                        throw new InvalidOperationException(
                            "Enumeration already finished");
                    }
                    if (cursor == -1) {
                        throw new InvalidOperationException(
                            "Enumeration not started");
                    }
                    return oThis.items[cursor];
                }
            }

            public void Dispose() {
                cursor = oThis.items.Length + 1;
            }

            public bool MoveNext() {
                ++cursor;
                if (cursor > (oThis.items.Length - 1)) {
                    return false;
                }
                return true;
            }

            public void Reset() {
                cursor = -1;
            }

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

            private int version;
            private int cursor;
            private SimpleCollection<__T> oThis;
        }


        private T[] items = null;
        private int version;
    }
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.88.62