问题描述
我如何编写一个自定义的 IEnumerator<T>
实现,它需要维护一些状态,并且仍然可以使用迭代器块来简化它?我能想到的最好的是这样的:
How can I write a custom IEnumerator<T>
implementation which needs to maintain some state and still get to use iterator blocks to simplify it? The best I can come up with is something like this:
public class MyEnumerator<T> : IEnumerator<T> {
private IEnumerator<T> _enumerator;
public int Position {get; private set;} // or some other custom properties
public MyEnumerator() {
Position = 0;
_enumerator = MakeEnumerator();
}
private IEnumerator<T> MakeEnumerator() {
// yield return something depending on Position
}
public bool MoveNext() {
bool res = _enumerator.MoveNext();
if (res) Position++;
return res;
}
// delegate Reset and Current to _enumerator as well
}
public class MyCollection<T> : IEnumerable<T> {
IEnumerator<T> IEnumerable<T>.GetEnumerator() {
return GetEnumerator();
}
public MyEnumerator<T> GetEnumerator() {
return new MyEnumerator<T>();
}
...
}
推荐答案
为什么要写一个迭代器类?迭代器块的全部意义在于您不必...
Why do you want to write an iterator class? The whole point of an iterator block is so you don't have to...
即
public IEnumerator<T> GetEnumerator() {
int position = 0; // state
while(whatever) {
position++;
yield return ...something...;
}
}
如果您添加更多上下文(即,为什么上述内容不起作用),我们可能会提供更多帮助.
If you add more context (i,e, why the above can't work), we can probably help more.
但如果可能,请避免编写迭代器类.它们的工作量很大,而且很容易出错.
But if possible, avoid writing an iterator class. They are lots of work, and easy to get wrong.
顺便说一句,您不必真正为 Reset
烦恼 - 它在很大程度上已被弃用,并且不应该真正使用(因为不能依赖它来为任意枚举数).
By the way, you don't really have to bother with Reset
- it is largely deprecated, and shouldn't really ever be used (since it can't be relied to work for an arbitrary enumerator).
如果您想使用内部迭代器,那也可以:
If you want to consume an inner iterator, that is fine too:
int position = 0;
foreach(var item in source) {
position++;
yield return position;
}
或者如果你只有一个枚举器:
or if you only have an enumerator:
while(iter.MoveNext()) {
position++;
yield return iter.Current;
}
您也可以考虑将状态(作为元组)添加到您生成的内容中:
You might also consider adding the state (as a tuple) to the thing you yield:
class MyState<T> {
public int Position {get;private set;}
public T Current {get;private set;}
public MyState(int position, T current) {...} // assign
}
...
yield return new MyState<Foo>(position, item);
最后,您可以使用 LINQ 样式的扩展/委托方法,使用 Action<int,T>
向调用者提供位置和值:
Finally, you could use a LINQ-style extension/delegate approach, with an Action<int,T>
to supply the position and value to the caller:
static void Main() {
var values = new[] { "a", "b", "c" };
values.ForEach((pos, s) => Console.WriteLine("{0}: {1}", pos, s));
}
static void ForEach<T>(
this IEnumerable<T> source,
Action<int, T> action) {
if (source == null) throw new ArgumentNullException("source");
if (action == null) throw new ArgumentNullException("action");
int position = 0;
foreach (T item in source) {
action(position++, item);
}
}
输出:
0: a
1: b
2: c
这篇关于编写自定义 IEnumerator<T>带迭代器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!