patterncsharpMinor
Implementing Peek to IEnumerator and IEnumerator<T>
Viewed 0 times
andimplementingpeekienumerator
Problem
Many of you might have come to the point and wished to have a Peek for IEnumerator and IEnumerator. I tried to implement it by cheating a bit and looking up the next element before the actual MoveNext call. So I ended up with some kind of wrapper.
First of the extensions to convert default enumerators:
And here is the non-generic 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++;
First of the extensions to convert default enumerators:
public static class PeekableEnumeratorExtension
{
public static PeekableEnumerator ToPeekable(this IEnumerator enumerator)
{
return new PeekableEnumerator(enumerator);
}
public static PeekableEnumerator ToPeekable(this IEnumerator enumerator)
{
return new PeekableEnumerator(enumerator);
}
}And here is the non-generic 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++;
Solution
I think your implementation is too complicated, and what nagged me was that you start enumerate in constructor. Here is my implementation which fix that. The state reduced to a boolean telling that the peek value has been fetched from the underlying enumerator or not.
My test to make sure it complies to your needed behaviour:
public class PeekEnumerator : IEnumerator
{
private IEnumerator _enumerator;
private T _peek;
private bool _didPeek;
public PeekEnumerator(IEnumerator 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;
}
}
}My test to make sure it complies to your needed behaviour:
var a = new PeekEnumerator(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());
}Code Snippets
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());
}Context
StackExchange Code Review Q#32857, answer score: 7
Revisions (0)
No revisions yet.