有没有办法使用yield块来实现IEnumerator<T>
,它可以向后(MoveLast()
)以及向前?
答案 0 :(得分:5)
不,C#编译器生成的状态机是严格转发的。
在许多情况下倒退甚至没有意义。想象一下从网络流中读取迭代器 - 要向后移动,它必须记住它曾读过的所有内容,因为它无法回放时间并再次向网络询问数据。
(同样以任何有损方式生成数据的任何东西。想象一下迭代器在每次迭代时为Conway的Life返回一个新的板 - 有多个板可能都是之前的一个,所以为了倒退,你必须记住你已经归还的东西。)
答案 1 :(得分:4)
不直接来自迭代器块,没有。
但是,调用者总是可以缓冲结果,例如缓冲到List<T>
,或者只调用Reverse()
- 但这并不总是适用。
答案 2 :(得分: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循环,则可以反转方向。
或者......如果你想反向(不是双向)“预先”列表,你可以利用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];
}
或者......如果你想通过漫长的路线反向前进,你可以实现自己的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; }
}
}
答案 3 :(得分:1)
C5收藏库(http://www.itu.dk/research/c5/) 使用向后枚举实现集合和链表。该项目是OpenSource,所以你应该能够在那里找到答案。
答案 4 :(得分:1)
没有。 IEnumerator的一个限制是它保持当前状态,并且它不记得它的先前状态。因此,IEnumerable是仅向前的。
如果你需要保持先前的状态,请将IEnumerable读入List或LinkedList,然后通过这些对象进行枚举。
答案 5 :(得分:1)
实际上,Accelerated C# 2008中似乎有一种方法。不幸的是,在预览中看不到两个页面,它必须依赖于反射(其结果可以像往常一样缓存),但是你可以获得要点。
答案 6 :(得分:0)
没有。使用yield
会产生IEnumerable
,这是单向的。