6

有没有办法使用 yield 块来实现一个IEnumerator<T>可以向后(MoveLast())以及向前的方法?

4

7 回答 7

5

不,C#编译器生成的状态机是严格向前的。

在许多情况下,甚至倒退也没有意义。想象一个从网络流中读取的迭代器——要倒退,它必须记住它曾经读取过的所有内容,因为它无法倒回时间并再次向网络请求数据。

(同上任何以某种有损方式生成数据的东西。想象一个迭代器,它在每次迭代中为 Conway's Life 返回一个新板 - 有多个板可能都是一个板,所以要向后退,你必须再次记住你'已经回来了。)

于 2009-01-16T16:50:20.227 回答
5

不是直接来自迭代器块,不。

然而,调用者总是可以缓冲结果,例如到 aList<T>中,或者只是调用Reverse()- 但这并不总是适用。

于 2009-01-16T16:53:45.273 回答
3

我知道这个线程非常旧,但需要注意的是

foreach(var item in someCollection)
{
    // Do something
}

...被编译成:

var enumerator = someCollection.GetEnumerator()
while (enumerator.MoveNext())
{
    var item = enumerator.Current;
    // Do something
}

因此,如果您不介意“MoveNext”语法,您可以轻松实现 IEnumerator 并添加“MovePrevious”。如果您使用“foreach”,您将无法反转方向,但如果使用 while 循环,您将能够反转方向。

或者......如果你想以相反的方向(不是双向)“foreach”一个列表,你可以利用 yield 语句。

public static IEnumerable<TItem> Get<TItem>(IList<TItem> list)
{
    if (list == null)
        yield break;

    for (int i = list.Count - 1; i > -1; i--)
        yield return list[i];
}

或者...如果您想通过长途旅行来反向进行 foreach,您可以实现自己的 IEnumerable/IEnumerator

public static class ReverseEnumerable
{
    public static IEnumerable<TItem> Get<TItem>(IList<TItem> list)
    {
        return new ReverseEnumerable<TItem>(list);
    }
}

public struct ReverseEnumerable<TItem> : IEnumerable<TItem>
{
    private readonly IList<TItem> _list;

    public ReverseEnumerable(IList<TItem> list)
    {
        this._list = list;
    }

    public IEnumerator<TItem> GetEnumerator()
    {
        if (this._list == null)
            return Enumerable.Empty<TItem>().GetEnumerator();

        return new ReverseEnumator<TItem>(this._list);
    }

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

public struct ReverseEnumator<TItem> : IEnumerator<TItem>
{
    private readonly IList<TItem> _list;
    private int _currentIndex;

    public ReverseEnumator(IList<TItem> list)
    {
        this._currentIndex = list.Count;
        this._list = list;
    }

    public bool MoveNext()
    {
        if (--this._currentIndex > -1)
            return true;

        return false;
    }

    public void Reset()
    {
        this._currentIndex = -1;
    }

    public void Dispose() { }

    public TItem Current
    {
        get
        {
            if (this._currentIndex < 0)
                return default(TItem);

            if (this._currentIndex >= this._list.Count)
                return default(TItem);

            return this._list[this._currentIndex];
        }
    }

    object IEnumerator.Current
    {
        get { return this.Current; }
    }
}
于 2015-04-22T16:36:22.510 回答
1

C5 Collections 库 ( http://www.itu.dk/research/c5/ ) 使用反向枚举实现集合和链表。该项目是开源的,所以你应该能够在那里找到答案。

于 2009-01-16T16:54:02.933 回答
1

不可以。IEnumerator 的限制之一是它保持其当前状态,并且不记得其先前状态。因此,IEnumerable 是只进的。

如果您需要保留先前的状态,请将 IEnumerable 读入 List 或 LinkedList 并枚举这些对象。

于 2009-01-16T16:56:31.557 回答
1

实际上, Accelerated C# 2008中似乎描述了一种方法。不幸的是,两个页面在预览中是不可见的,它必须依赖于反射(其结果可以像往常一样被缓存)但你可以得到要点。

于 2009-01-16T17:29:07.777 回答
0

不,使用yield结果IEnumerable是单向的。

于 2009-01-16T16:50:25.000 回答