你们中的许多人可能已经说到点子上了,并希望对IEnumerator和IEnumerator有一个了解。我试图通过欺骗一点并在实际的MoveNext调用之前查找下一个元素来实现它。所以我最后用了某种包装纸。
首先,要转换默认枚举数的扩展:
public static class PeekableEnumeratorExtension
{
public static PeekableEnumerator ToPeekable(this IEnumerator enumerator)
{
return new PeekableEnumerator(enumerator);
}
public static PeekableEnumerator<T> ToPeekable<T>(this IEnumerator<T> enumerator)
{
return new PeekableEnumerator<T>(enumerator);
}
}下面是非泛型PeekableEnumerator:
public class PeekableEnumerator : IEnumerator
{
protected enum Status { Uninitialized, Starting, Started, Ending, Ended }
protected IEnumerator enumerator;
protected Status status;
protected object current;
protected object peek;
public PeekableEnumerator(IEnumerator enumerator)
{
this.enumerator = enumerator;
status = Status.Uninitialized;
MoveNext();
}
public object Current
{
get
{
if (Status.Starting == status)
throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
if (Status.Ended == status)
throw new InvalidOperationException("Enumeration already finished.");
return current;
}
}
public object Peek
{
get
{
if (Status.Ending == status || Status.Ended == status)
throw new InvalidOperationException("Enumeration already finished.");
return peek;
}
}
public bool MoveNext()
{
current = peek;
switch (status)
{
case Status.Uninitialized:
case Status.Starting:
if (enumerator.MoveNext())
{
status++;
peek = enumerator.Current;
}
else
status = Status.Ending;
break;
case Status.Started:
if (enumerator.MoveNext())
peek = enumerator.Current;
else
status++;
break;
case Status.Ending:
status++;
break;
}
return Status.Ended != status;
}
public void Reset()
{
enumerator.Reset();
status = Status.Uninitialized;
MoveNext();
}
}非常类似的PeekableEnumerator:
public class PeekableEnumerator<T> : IEnumerator<T>
{
protected enum Status { Uninitialized, Starting, Started, Ending, Ended }
protected IEnumerator<T> enumerator;
protected Status status;
protected T current;
protected T peek;
public PeekableEnumerator(IEnumerator<T> enumerator)
{
this.enumerator = enumerator;
status = Status.Uninitialized;
MoveNext();
}
public T Current
{
get
{
if (Status.Starting == status)
throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
if (Status.Ended == status)
throw new InvalidOperationException("Enumeration already finished.");
return current;
}
}
object System.Collections.IEnumerator.Current { get { return Current; } }
public T Peek
{
get
{
if (Status.Ending == status || Status.Ended == status)
throw new InvalidOperationException("Enumeration already finished.");
return peek;
}
}
public bool MoveNext()
{
current = peek;
switch (status)
{
case Status.Uninitialized:
case Status.Starting:
if (enumerator.MoveNext())
{
status++;
peek = enumerator.Current;
}
else
status = Status.Ending;
break;
case Status.Started:
if (enumerator.MoveNext())
peek = enumerator.Current;
else
status++;
break;
case Status.Ending:
status++;
break;
}
return Status.Ended != status;
}
public void Reset()
{
enumerator.Reset();
status = Status.Uninitialized;
MoveNext();
}
public void Dispose()
{
enumerator.Dispose();
}
}在你问:为什么会有5种状态?它来源于电流和Peek的生命周期:
Status | Current | Peek | Comment
--------------+-----------+-----------+-----------------------------------
Uninitialized | n/a | n/a | Internal for constructor and Reset
Starting | Exception | Available | Before first MoveNext
Started | Available | Available | After first MoveNext
Ending | Available | Exception | wrapped MoveNext returned false
Ended | Exception | Exception | After enumeration finished示例用法:
var a = new[] { 1, 2, 3 }.GetEnumerator().ToPeekable();
a.Current; // InvalidOperationException
a.Peek; // 1
a.MoveNext(); // true
a.Current; // 1
a.Peek; // 2
a.MoveNext(); // true
a.Current; // 2
a.Peek; // 3
a.MoveNext(); // true
a.Current; // 3
a.Peek; // InvalidOperationException
a.MoveNext(); // false
a.Current; // InvalidOperationException
a.Peek; // InvalidOperationException多亏了斯维克,这里有一个使用队列的替代版本。它将基本用法从IEnumerator更改为ICollection作为输入,但我可以接受。我需要保留一份原始收藏的副本,以便重新设置。
public class PeekableEnumerator : IEnumerator
{
protected ICollection collection;
protected Queue queue;
protected bool current_set;
protected object current;
protected bool peek_set;
protected object peek;
public object Current
{
get
{
if (!current_set)
if (peek_set)
throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
else
throw new InvalidOperationException("Enumeration already finished.");
return current;
}
}
public object Peek
{
get
{
if (!peek_set)
throw new InvalidOperationException("Enumeration already finished.");
return peek;
}
}
public PeekableEnumerator(ICollection collection)
{
this.collection = collection;
Reset();
}
public bool MoveNext()
{
current_set = peek_set;
current = peek;
if (0 == queue.Count)
{
peek_set = false;
return current_set;
}
else
{
peek_set = true;
peek = queue.Dequeue();
return true;
}
}
public void Reset()
{
queue = new Queue(collection);
MoveNext();
}
}发布于 2013-10-21 17:14:12
我认为您的实现太复杂了,让我烦恼的是您开始在构造函数中枚举。这是我的实现,它解决了这个问题。状态简化为布尔值,说明peek值是否已从基础枚举数中获取。
public class PeekEnumerator<T> : IEnumerator<T>
{
private IEnumerator<T> _enumerator;
private T _peek;
private bool _didPeek;
public PeekEnumerator(IEnumerator<T> enumerator)
{
if (enumerator == null)
throw new ArgumentNullException("enumerator");
_enumerator = enumerator;
}
#region IEnumerator implementation
public bool MoveNext()
{
return _didPeek ? !(_didPeek = false) : _enumerator.MoveNext();
}
public void Reset()
{
_enumerator.Reset();
_didPeek = false;
}
object IEnumerator.Current { get { return this.Current; } }
#endregion
#region IDisposable implementation
public void Dispose()
{
_enumerator.Dispose();
}
#endregion
#region IEnumerator implementation
public T Current
{
get { return _didPeek ? _peek : _enumerator.Current; }
}
#endregion
private void TryFetchPeek() {
if (!_didPeek && (_didPeek = _enumerator.MoveNext()))
{
_peek = _enumerator.Current;
}
}
public T Peek
{
get {
TryFetchPeek();
if (!_didPeek)
throw new InvalidOperationException("Enumeration already finished.");
return _peek;
}
}
}我的测试以确保它符合你所需要的行为:
var a = new PeekEnumerator<int>(new [] { 1, 2, 3 }.AsEnumerable().GetEnumerator());
Console.WriteLine(a.Peek); // 1
Console.WriteLine(a.MoveNext()); // true
Console.WriteLine(a.Current); // 1
Console.WriteLine(a.Peek); // 2
Console.WriteLine(a.MoveNext()); // true
Console.WriteLine(a.Current); // 2
Console.WriteLine(a.Peek); // 3
Console.WriteLine(a.MoveNext()); // true
Console.WriteLine(a.Current); // 3
try {
Console.WriteLine(a.Peek); // InvalidOperationException
}
catch (Exception e) {
Console.WriteLine(e.GetType());
}
Console.WriteLine(a.MoveNext()); // false
try {
Console.WriteLine(a.Current); // InvalidOperationException
}
catch (Exception e) {
Console.WriteLine(e.GetType());
}
try {
Console.WriteLine(a.Peek); // InvalidOperationException
}
catch (Exception e) {
Console.WriteLine(e.GetType());
}发布于 2013-10-18 08:28:51
不太清楚您的问题是什么,但我有一些您可能需要考虑的问题,也许Peek()不应该在超出界限或有Status.Ending == status || Status.Ended == status时抛出异常,而是返回一个空对象,以便在您决定做某事之前检查它的值。
if(a.Peek != null) { ... }思想食粮
发布于 2015-03-25 18:51:52
我更改了svick的含义以使用泛型,并将peek方法更改为返回bool,而不是抛出异常。虽加
bool TryPeek(out T value);对于现有的实现可能更合适。
public class PeekableEnumerator<T> : IEnumerator<T>
{
protected IEnumerable<T> collection;
protected Queue<T> queue;
protected bool current_set;
protected T current;
protected bool peek_set;
protected T peek;
public T Current
{
get
{
if (!current_set)
if (peek_set)
throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
else
throw new InvalidOperationException("Enumeration already finished.");
return current;
}
}
public bool Peek(out T value)
{
value = default(T);
if (!peek_set)
return false;
value = peek;
return true;
}
public PeekableEnumerator(IEnumerable<T> collection)
{
this.collection = collection;
Reset();
}
public bool MoveNext()
{
current_set = peek_set;
current = peek;
if (0 == queue.Count)
{
peek_set = false;
return current_set;
}
else
{
peek_set = true;
peek = queue.Dequeue();
return true;
}
}
public void Reset()
{
queue = new Queue<T>(collection);
MoveNext();
}
public virtual void Dispose()
{
}
object System.Collections.IEnumerator.Current
{
get { return Current; }
}
}https://codereview.stackexchange.com/questions/32857
复制相似问题