Как бы вы реализовали интерфейс IEnumerator?

У меня есть класс, который сопоставляет объекты с объектами, но, в отличие от словаря, он сопоставляет их в обоих направлениях. Сейчас я пытаюсь реализовать собственный интерфейс IEnumerator, который перебирает значения.

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();
    }
}

person Tomas Pajonk    schedule 10.09.2008    source источник


Ответы (5)


Во-первых, не заставляйте объект коллекции реализовывать IEnumerator‹>. Это приводит к ошибкам. (Рассмотрите ситуацию, когда два потока перебирают одну и ту же коллекцию).

Корректная реализация перечислителя оказывается нетривиальной задачей, поэтому в C# 2.0 для этого добавлена ​​специальная языковая поддержка, основанная на операторе 'yield return'.

Недавняя серия сообщений в блоге Рэймонда Чена («Реализация итераторов в C# и ее последствия») — хорошее место для ознакомления.

person Jay Bazuzi    schedule 10.09.2008

Просто реализуйте интерфейс IEnumerable<T>. Нет необходимости реализовывать IEnumerator<T>, если вы не хотите делать какие-то специальные вещи в перечислителе, что в вашем случае кажется не нужным.

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

и это все.

person Pop Catalin    schedule 10.09.2008
comment
Сначала я сделал это так, но позже понял, что хотел бы использовать для каждого этого объекта, чтобы получить значения. - person Tomas Pajonk; 10.09.2008
comment
Чтобы использовать foreach для объекта, единственное требование состоит в том, чтобы у него был публичный метод GetEnumerator(), который возвращает значение, и IEnumerator, объект вообще не должен реализовывать какой-либо интерфейс, но рекомендуется реализовать IEnumerable или IEnumerable‹T› , вы можете догадаться, почему ;) - person Pop Catalin; 10.09.2008
comment
Я так и думал, значит моя ошибка в другом. Спасибо за успокоение. - person Tomas Pajonk; 10.09.2008

CreateEnumerable() возвращает IEnumerable, который реализует 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
person Jack    schedule 21.02.2013


Вот пример из книги «Алгоритмы (4-е издание) Роберта Седжвика».

Он был написан на Java, и я в основном переписал его на 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; }
        }
    }
}
person Raz Megrelidze    schedule 03.05.2014