如果你想利用一個IEnumerator<T>
,並得到一個IEnumerable<T>
代表剩下的順序,從字面上看,你將不得不做一些魔術才能到達那裏。
其原因在於,一般來說,枚舉可以枚舉多次,而枚舉器不能,它只是其中的一個「多次」本身。
首先,您可以嘗試弄清楚您正在處理的是哪種集合,從而在原始枚舉器的其餘部分上返回適當的枚舉器。你要去的原因。
或...您可以將枚舉數的其餘部分緩存到新集合中並返回該集合。這當然會消耗你的原始枚舉器,無論可能是什麼,並且在時間或內存方面可能會很昂貴。
或者......你可以做幾件事提出的建議,不要實際返回枚舉器,而是使用可枚舉類的Skip和Take方法來返回你想要的結果。這將返回一個新的枚舉值,每次枚舉時,它將枚舉原始枚舉值,跳過前兩個項目,併產生其餘項目。
讓我改寫最後一段。如果您不嘗試將其餘的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
這是一個恥辱,沒有'上的IEnumerable Clone'方法。這可能會幫助你。你可以實現一個EnumerableEx 類包裝一個IEnumerable 和支持克隆。 –
2010-05-13 20:15:57
'休息= sequence.Skip(2)'會複製你的例子,但並不能一概而論... – jball 2010-05-13 20:16:55