問題描述
我如何編寫一個自定義的 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>帶迭代器的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!