16

With Java Iterators, I have used the hasNext method to determine whether an iteration has more elements (without consuming an element) -- thus, hasNext is like a "Peek" method.

My question: is there anything like a "hasNext" or "Peek" method with C#'s generic IEnumerators?

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
JaysonFix
  • 2,515
  • 9
  • 28
  • 28

6 Answers6

36

No, but in C# you can repeatedly ask for the current element without moving to the next one. It's just a different way of looking at it.

It wouldn't be too hard to write a C# class to take a .NET-style IEnumerator and return a Java-style Iterator. Personally I find the .NET style easier to use in most cases, but there we go :)

EDIT: Okay, this is completely untested, but I think it will work. It does at least compile :)

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();
    }
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • (I'm happy to code up the adapter if anyone's interested, but I won't do it otherwise...) – Jon Skeet Aug 13 '09 at 16:25
  • 3
    I'd be interested in seeing it, Jon. – JaysonFix Aug 13 '09 at 16:31
  • Wow, that was fast! Thank you, Jon! – JaysonFix Aug 13 '09 at 17:04
  • @Andrew, it places responsibility a little different. I find the dotNet version easier to work with. – Dykam Aug 13 '09 at 17:15
  • The major failing of Java's iterators is that they don't have the equivalent of IDisposable, making them impossible to use for anything which might iterator over a resource (e.g. lines in a file). – Jon Skeet Aug 13 '09 at 17:40
  • Why do you have 4 classes and not just 2? I would think that you just need classes that wrap IEnumerable and IEnumerator. What's the IIterator business for? – Eyal Jun 24 '12 at 04:06
  • @Eyal: They're just mappings for the Java interfaces. You don't *have* to have them of course - but using the concrete class here would *force* you to always use the wrapper, even if occasionally you wanted to implement the Java style directly. – Jon Skeet Jun 24 '12 at 06:41
18

No, unfortunately there isn't.

The IEnumerator<T> interface only exposes the following members:

Methods:

Dispose
MoveNext
Reset

Properties:

Current

Andrew Hare
  • 344,730
  • 71
  • 640
  • 635
3

Enumerators are often lazily evaluated so HasNext makes little sense.

Ray
  • 1,585
  • 9
  • 10
2

Nope, just MoveNext, Reset and Current.

Matt Howells
  • 40,310
  • 20
  • 83
  • 102
2

You can also try having a look at this Implementing Peek to IEnumerator and IEnumerator<>. It's an extension method that adds the Peek functionality to IEnumerator. Hope it helps. :)

Community
  • 1
  • 1
Bryida
  • 482
  • 6
  • 9
0

Use good old manual iteration

        // 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
            {
            }
        }
Phil
  • 499
  • 4
  • 4