Есть ли метод HasNext для IEnumerator?

С Java Iterators я использовал метод hasNext, чтобы определить, имеет ли итерация больше элементов (без использования элемента) - таким образом, hasNext подобен методу "Peek".

Мой вопрос: есть ли что-нибудь вроде метода "hasNext" или "Peek" с общими IEnumerators С#?


person JaysonFix    schedule 13.08.2009    source источник
comment
Как ты мог не знать? Возможно ли, что вы никогда не видели msdn.microsoft.com/en-us/ библиотека/78dfe2yb.aspx?   -  person John Saunders    schedule 13.08.2009
comment
Большое спасибо, что указали на это, мистер Сондерс.   -  person JaysonFix    schedule 13.08.2009


Ответы (6)


Нет, к сожалению, нет.

Интерфейс IEnumerator<T> предоставляет только следующие элементы:

Методы:

Dispose
MoveNext
Reset

Свойства:

Current

person Andrew Hare    schedule 13.08.2009
comment
Мы говорим об IEnumerator, а не об IEnumerable, верно? И * должен быть на Dispose, а не на MoveNext. - person Even Mien; 13.08.2009
comment
@Even - Да, этот пост был полон ошибок! Спасибо, что указали на них. - person Andrew Hare; 13.08.2009

Нет, но в C# вы можете многократно запрашивать текущий элемент, не переходя к следующему. Это просто другой взгляд на это.

Было бы несложно слишком написать класс C#, который бы брал IEnumerator в стиле .NET и возвращал Iterator в стиле Java. Лично я считаю, что стиль .NET в большинстве случаев проще в использовании, но поехали :)

РЕДАКТИРОВАТЬ: Хорошо, это полностью не проверено, но я думаю, что это сработает. По крайней мере компилируется :)

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

// // Mimics Java's Iterable<T> interface
public interface IIterable<T>
{
    IIterator<T> Iterator();
}

// Mimics Java's Iterator interface - but
// implements IDisposable for the sake of
// parity with IEnumerator.
public interface IIterator<T> : IDisposable
{
    bool HasNext { get; }
    T Next();
    void Remove();
}

public sealed class EnumerableAdapter<T> : IIterable<T>
{
    private readonly IEnumerable<T> enumerable;

    public EnumerableAdapter(IEnumerable<T> enumerable)
    {
        this.enumerable = enumerable;
    }

    public IIterator<T> Iterator()
    {
        return new EnumeratorAdapter<T>(enumerable.GetEnumerator());
    }
}

public sealed class EnumeratorAdapter<T> : IIterator<T>
{
    private readonly IEnumerator<T> enumerator;

    private bool fetchedNext = false;
    private bool nextAvailable = false;
    private T next;

    public EnumeratorAdapter(IEnumerator<T> enumerator)
    {
        this.enumerator = enumerator;
    }

    public bool HasNext
    {
        get
        {
            CheckNext();
            return nextAvailable;
        } 
    }

    public T Next()
    {
        CheckNext();
        if (!nextAvailable)
        {
            throw new InvalidOperationException();
        }
        fetchedNext = false; // We've consumed this now
        return next;
    }

    void CheckNext()
    {
        if (!fetchedNext)
        {
            nextAvailable = enumerator.MoveNext();
            if (nextAvailable)
            {
                next = enumerator.Current;
            }
            fetchedNext = true;            
        }
    }

    public void Remove()
    {
        throw new NotSupportedException();
    }

    public void Dispose()
    {
        enumerator.Dispose();
    }
}

public sealed class IterableAdapter<T> : IEnumerable<T>
{
    private readonly IIterable<T> iterable;

    public IterableAdapter(IIterable<T> iterable)
    {
        this.iterable = iterable;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return new IteratorAdapter<T>(iterable.Iterator());
    }

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

public sealed class IteratorAdapter<T> : IEnumerator<T>
{
    private readonly IIterator<T> iterator;

    private bool gotCurrent = false;
    private T current;

    public IteratorAdapter(IIterator<T> iterator)
    {
        this.iterator = iterator;
    }

    public T Current
    {
        get
        {
            if (!gotCurrent)
            {
                throw new InvalidOperationException();
            }
            return current;
        }
    }

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

    public bool MoveNext()
    {
        gotCurrent = iterator.HasNext;
        if (gotCurrent)
        {
            current = iterator.Next();
        }
        return gotCurrent;
    }

    public void Reset()
    {
        throw new NotSupportedException();
    }

    public void Dispose()
    {
        iterator.Dispose();
    }
}
person Jon Skeet    schedule 13.08.2009
comment
(Я рад написать код адаптера, если кому-то это интересно, но иначе я этого делать не буду...) - person Jon Skeet; 13.08.2009
comment
Мне было бы интересно на это посмотреть, Джон. - person JaysonFix; 13.08.2009
comment
Вау, это было быстро! Спасибо, Джон! - person JaysonFix; 13.08.2009
comment
@ Эндрю, это возлагает ответственность немного по-другому. Я считаю, что с версией dotNet легче работать. - person Dykam; 13.08.2009
comment
Основной недостаток итераторов Java заключается в том, что у них нет эквивалента IDisposable, что делает невозможным их использование для чего-либо, что может выполнять итерацию по ресурсу (например, строки в файле). - person Jon Skeet; 13.08.2009
comment
Почему у вас 4 класса, а не только 2? Я бы подумал, что вам просто нужны классы, которые обертывают IEnumerable и IEnumerator. Для чего нужен IIterator? - person Eyal; 24.06.2012
comment
@Eyal: это просто сопоставления для интерфейсов Java. Конечно, вам необходимо их иметь, но использование здесь конкретного класса вынуждает всегда использовать оболочку, даже если иногда вы хотите реализовать стиль Java напрямую. - person Jon Skeet; 24.06.2012

Перечислители часто оцениваются лениво, поэтому HasNext не имеет особого смысла.

person Ray    schedule 13.08.2009

Нет, только MoveNext, Reset и Current.

person Matt Howells    schedule 13.08.2009

Вы также можете попробовать взглянуть на этот Реализация Peek для IEnumerator и IEnumerator‹>. Это метод расширения, который добавляет функциональные возможности Peek в IEnumerator. Надеюсь, поможет. :)

person Bryida    schedule 24.08.2016

Используйте старую добрую ручную итерацию

        // IEnumerable<>
        for (int i = 0; i < enumerable.Count(); i++)
        {
            var item = enumerable.ElementAt(i);

            if(i + 1 < enumerable.Count()) // eq. Iterator.HasNext
            {
            }
        }

        // IList<>
        for (int i = 0; i < list.Count; i++)
        {
            var item = list[1];

            if (i + 1 < list.Count) // eq. Iterator.HasNext
            {
            }
        }
person Phil    schedule 23.04.2020