有没有办法使用 yield 块来实现一个IEnumerator<T>
可以向后(MoveLast()
)以及向前的方法?
7 回答
不,C#编译器生成的状态机是严格向前的。
在许多情况下,甚至倒退也没有意义。想象一个从网络流中读取的迭代器——要倒退,它必须记住它曾经读取过的所有内容,因为它无法倒回时间并再次向网络请求数据。
(同上任何以某种有损方式生成数据的东西。想象一个迭代器,它在每次迭代中为 Conway's Life 返回一个新板 - 有多个板可能都是前一个板,所以要向后退,你必须再次记住你'已经回来了。)
不是直接来自迭代器块,不。
然而,调用者总是可以缓冲结果,例如到 aList<T>
中,或者只是调用Reverse()
- 但这并不总是适用。
我知道这个线程非常旧,但需要注意的是
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; }
}
}
C5 Collections 库 ( http://www.itu.dk/research/c5/ ) 使用反向枚举实现集合和链表。该项目是开源的,所以你应该能够在那里找到答案。
不可以。IEnumerator 的限制之一是它保持其当前状态,并且不记得其先前状态。因此,IEnumerable 是只进的。
如果您需要保留先前的状态,请将 IEnumerable 读入 List 或 LinkedList 并枚举这些对象。
实际上, Accelerated C# 2008中似乎描述了一种方法。不幸的是,两个页面在预览中是不可见的,它必须依赖于反射(其结果可以像往常一样被缓存)但你可以得到要点。
不,使用yield
结果IEnumerable
是单向的。