If you want to take an IEnumerator<T>
and get an IEnumerable<T>
representing the rest of the sequence, taken literally, you're going to have to do some magic to get there.
The reason for this is that in general sense, an enumerable can be enumerated multiple times, whereas an enumerator cannot, it is just one of those "multiple times" by itself.
First, you can try to figure out what kind of collection you're dealing with, and thus return an appropriate enumerator on top of the rest of the original enumerator. The reason you're going.
Or... you can just cache the rest of the enumerator into a new collection and return that. This will of course consume your original enumerator, whatever that might be, and could be expensive, in terms of time or memory.
Or... you can do what several have suggested, don't actually return the enumerator, but instead use the Skip and Take methods of the enumerable class to return what you want. This will return a new enumerable, that each time it is enumerated, it will enumerate over the original enumerable, skip the first two items, and produce the rest.
Let me reword that last paragraph. If you don't try to return the rest of the IEnumerator<T>
as a new enumerable, but instead just deal with the original collection, it becomes much easier to deal with.
Here's some code that caches the elements. It has the benefit that if you produce 2 or more enumerators (or even just 1) from the resulting enumerable, and then let the enumerable go out of scope, as the enumerators start moving through the elements, it will allow the garbage collector to start collecting the elements that have been passed by.
In other words, if you do this:
var enumerable = enumerator.Remaining();
var enumerator1 = enumerable.GetEnumerator();
var enumerator2 = enumerable.GetEnumerator();
enumerator1.MoveNext();
enumerator2.MoveNext();
<-- at this point, enumerable is no longer used, and the first (head) element
of the enumerable is no longer needed (there's no way to get to it)
it can be garbage collected.
Of course, if you keep the enumerable around, and enumerate over all the elements in it, it will produce an in-memory copy of all the elements from the original enumerable, which, as I said, can be be costly.
Anyway, here's the code. It is not thread-safe:
using System;
using System.Collections.Generic;
using System.Collections;
namespace SO2829956
{
public class EnumeratorEnumerable<T> : IEnumerable<T>
{
private class Node
{
public T Value;
public Node Next;
}
private class Enumerator : IEnumerator<T>
{
private IEnumerator<T> _Enumerator;
private Node _Current;
public Enumerator(IEnumerator<T> enumerator, Node headElement)
{
_Enumerator = enumerator;
_Current = headElement;
}
public T Current
{
get { return _Current.Value; }
}
public void Dispose()
{
_Enumerator.Dispose();
}
object IEnumerator.Current
{
get { return Current; }
}
public bool MoveNext()
{
if (_Current.Next != null)
{
_Current = _Current.Next;
return true;
}
else if (_Enumerator.MoveNext())
{
_Current.Next = new Node
{
Value = _Enumerator.Current
};
_Current = _Current.Next;
return true;
}
else
{
_Enumerator.Dispose();
return false;
}
}
public void Reset()
{
throw new NotImplementedException();
}
}
private IEnumerator<T> _Enumerator;
private Node _FirstElement;
public EnumeratorEnumerable(IEnumerator<T> enumerator)
{
_Enumerator = enumerator;
_FirstElement = new Node
{
Next = null,
Value = enumerator.Current
};
}
public IEnumerator<T> GetEnumerator()
{
return new Enumerator(_Enumerator, _FirstElement);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public static class EnumeratorExtensions
{
public static IEnumerable<T> Remaining<T>(
this IEnumerator<T> enumerator)
{
return new EnumeratorEnumerable<T>(enumerator);
}
}
class Program
{
static void Main(string[] args)
{
List<int> values = new List<int> { 1, 2, 3, 4, 5 };
IEnumerator<int> enumerator = values.GetEnumerator();
enumerator.MoveNext();
enumerator.MoveNext();
var enumerable = enumerator.Remaining();
foreach (var i in enumerable)
Console.Out.WriteLine(i);
foreach (var i in enumerable)
Console.Out.WriteLine(i);
}
}
}
The output of running this program is:
3
4
5
3
4
5