12

I have a class that map objects to objects, but unlike dictionary it maps them both ways. I am now trying to implement a custom IEnumerator interface that iterates through the values.

public class Mapper<K,T> : IEnumerable<T>, IEnumerator<T>

{
    C5.TreeDictionary<K,T> KToTMap = new TreeDictionary<K,T>();
    C5.HashDictionary<T,K> TToKMap = new HashDictionary<T,K>();

    public void Add(K key, T value)
    {
        KToTMap.Add(key, value);
        TToKMap.Add(value, key);

    }

    public int Count
    {
        get { return KToTMap.Count; }
    }
    

    public K this[T obj]
    {
        get
        {
            return TToKMap[obj];
        }
    }

    public T this[K obj]
    {
        get
        {
            return KToTMap[obj];
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        return KToTMap.Values.GetEnumerator();
    }

    public T Current
    {
        get { throw new NotImplementedException(); }
    }

    public void Dispose()
    {
        throw new NotImplementedException();
    }

    object System.Collections.IEnumerator.Current
    {
        get { throw new NotImplementedException(); }
    }

    public bool MoveNext()
    {
        ;
    }

    public void Reset()
    {
        throw new NotImplementedException();
    }
}
Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
Tomas Pajonk
  • 5,132
  • 8
  • 41
  • 51

5 Answers5

19

First, don't make your collection object implement IEnumerator<>. This leads to bugs. (Consider the situation where two threads are iterating over the same collection).

Implementing an enumerator correctly turns out to be non-trivial, so C# 2.0 added special language support for doing it, based on the 'yield return' statement.

Raymond Chen's recent series of blog posts ("The implementation of iterators in C# and its consequences") is a good place to get up to speed.

Community
  • 1
  • 1
Jay Bazuzi
  • 45,157
  • 15
  • 111
  • 168
9

Just implement the IEnumerable<T> interface. No need to implement the IEnumerator<T> unless you want to do some special things in the enumerator, which for your case doesn't seem to be needed.

public class Mapper<K,T> : IEnumerable<T> {
    public IEnumerator<T> GetEnumerator()
    {
        return KToTMap.Values.GetEnumerator();
    }
}

and that's it.

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
Pop Catalin
  • 61,751
  • 23
  • 87
  • 115
  • That's the way I've done it at first, but later realized that I would like to use for each on this object to get to the values. – Tomas Pajonk Sep 10 '08 at 13:28
  • to use foreach on an object the only requirement is that it has a publi GetEnumerator() method that returns and IEnumerator, the object doesn't even have to implement any interface at all but it's recomended that you do implement IEnumerable or IEnumerable, you can guess why ;) – Pop Catalin Sep 10 '08 at 13:35
  • That's what I thought, so my error is elsewhere than. Thanks for reassurence. – Tomas Pajonk Sep 10 '08 at 13:39
6

CreateEnumerable() returns an IEnumerable which implements GetEnumerator()

public class EasyEnumerable : IEnumerable<int> {

    IEnumerable<int> CreateEnumerable() {
        yield return 123;
        yield return 456;
        for (int i = 0; i < 6; i++) {
            yield return i;
        }//for
    }//method

    public IEnumerator<int> GetEnumerator() {
        return CreateEnumerable().GetEnumerator();
    }//method

    IEnumerator IEnumerable.GetEnumerator() {
        return CreateEnumerable().GetEnumerator();
    }//method

}//class
Chris Mantle
  • 6,595
  • 3
  • 34
  • 48
Jack
  • 4,684
  • 2
  • 29
  • 22
4

Use yield return.

What is the yield keyword used for in C#?

Community
  • 1
  • 1
Mendelt
  • 36,795
  • 6
  • 74
  • 97
2

Here's an example from the book "Algorithms (4th Edition) by Robert Sedgewick".

It was written in java and i basically rewrote it in C#.

public class Stack<T> : IEnumerable<T>
{
    private T[] array;

    public Stack(int n)
    {
        array = new T[n];
    }

    public Stack()
    {
        array = new T[16];
    }

    public void Push(T item)
    {
        if (Count == array.Length)
        {
            Grow(array.Length * 2);
        }

        array[Count++] = item;
    }

    public T Pop()
    {
        if (Count == array.Length/4)
        {
            Shrink(array.Length/2);
        }

        return array[--Count];
    }

    private void Grow(int size)
    {
        var temp = array;
        array = new T[size];
        Array.Copy(temp, array, temp.Length);
    }

    private void Shrink(int size)
    {
        Array temp = array;
        array = new T[size];
        Array.Copy(temp,0,array,0,size);
    }

    public int Count { get; private set; }
    public IEnumerator<T> GetEnumerator()
    {
        return new ReverseArrayIterator(Count,array);
    }

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


    // IEnumerator implementation
    private class ReverseArrayIterator : IEnumerator<T>
    {
        private int i;

        private readonly T[] array;

        public ReverseArrayIterator(int count,T[] array)
        {
            i = count;
            this.array = array;
        }

        public void Dispose()
        {

        }

        public bool MoveNext()
        {
            return i > 0;
        }

        public void Reset()
        {

        }

        public T Current { get { return array[--i]; } }

        object IEnumerator.Current
        {
            get { return Current; }
        }
    }
}
Rezo Megrelidze
  • 2,877
  • 1
  • 26
  • 29