Если вы хотите взять IEnumerator<T>
и получить IEnumerable<T>
, представляющий остальную часть последовательности, взятую буквально, вам нужно будет сделать немного магии, чтобы добраться туда.
Причина этого в том, что в общем смысле перечисляемое может быть перечислено несколько раз, тогда как перечислитель не может, это просто один из этих "многократных" сам по себе.
Во-первых, вы можете попытаться выяснить, с какой коллекцией вы имеете дело, и таким образом вернуть соответствующий перечислитель поверх остального исходного перечислителя. Причина, по которой ты идешь.
Или ... вы можете просто кэшировать остаток перечислителя в новую коллекцию и вернуть его. Это, конечно, потребляет ваш оригинальный перечислитель, каким бы он ни был, и может быть дорогим, с точки зрения времени или памяти.
Или ... вы можете сделать то, что предложили несколько, на самом деле не возвращать перечислитель, а вместо этого использовать методы Skip и Take класса enumerable, чтобы вернуть то, что вы хотите. Это вернет новое перечисляемое значение, которое при каждом перечислении будет перечислять исходное перечисляемое, пропустить первые два элемента и произвести остальные.
Позвольте мне перефразировать последний абзац. Если вы не попытаетесь вернуть остальную часть IEnumerator<T>
в качестве нового перечислимого, а вместо этого будете иметь дело только с исходной коллекцией, с ней станет намного легче иметь дело.
Вот некоторый код, который кэширует элементы. Преимущество состоит в том, что если вы создадите 2 или более перечислителей (или даже просто 1) из полученного перечисляемого, а затем позволите перечислимому выйти из области видимости, поскольку перечислители начнут перемещаться по элементам, это позволит запустить сборщик мусора собирая элементы, которые прошли мимо.
Другими словами, если вы сделаете это:
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.
Конечно, если вы будете хранить перечислимый объект и перечислять все элементы в нем, он создаст в памяти копию всех элементов из исходного перечисляемого элемента, что, как я уже сказал, может быть дорогостоящим.
В любом случае, вот код. Это не потокобезопасно:
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);
}
}
}
Результат запуска этой программы:
3
4
5
3
4
5