IEnumerator是否有“HasNext”方法?

16

在Java中,我使用Iterator对象的hasNext方法来判断迭代器是否还有更多元素(同时不会消耗元素),因此hasNext方法就像一个"Peek"方法。

我的问题是:C#的泛型IEnumerator对象是否有类似于"hasNext"或"Peek"方法?


5
你怎么可能不知道呢?你从没看过 http://msdn.microsoft.com/en-us/library/78dfe2yb.aspx 吗? - John Saunders
非常感谢您指出这一点,Saunders先生。 - JaysonFix
6个回答

36
不过,在C#中,你可以重复询问当前元素而不需要移动到下一个元素。这只是一种不同的看待问题的方式。
编写一个C#类来将.NET风格的IEnumerator转换为Java风格的Iterator并不会太难。就我个人而言,在大多数情况下,我发现使用.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();
    }
}

如果有人感兴趣的话,我很高兴编写适配器,但如果没有兴趣的话,我不会这样做... - Jon Skeet
3
我很感兴趣看到它,Jon。 - JaysonFix
哇,速度真快!谢谢你,Jon! - JaysonFix
为什么你有4个类而不是只有2个?我认为你只需要包装IEnumerable和IEnumerator的类。IIterator是干嘛用的? - Eyal
@Eyal:它们只是Java接口的映射。当然,你不必拥有它们 - 但在这里使用具体类将强制你始终使用包装器,即使偶尔你想直接实现Java风格。 - Jon Skeet
显示剩余2条评论

19
很遗憾,没有这个选项。
IEnumerator<T> 接口只公开以下成员:
方法:

Dispose
MoveNext
Reset

属性:

Current


1
我们现在讨论的是IEnumerator,而不是IEnumerable,对吧?并且*应该放在Dispose上,而不是MoveNext。 - Even Mien
@Even - 哎呀,那篇帖子错漏百出!感谢你指出来。 - Andrew Hare

3

枚举器通常是惰性评估的,因此HasNext几乎没有意义。


2

2

不要,只需要使用MoveNextResetCurrent即可。


0

使用好老的手动迭代

        // 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
            {
            }
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接