IEnumerator<T>
文字通り、シーケンスの残りの部分を表すを取得したい場合はIEnumerable<T>
、そこに到達するためにいくつかの魔法を実行する必要があります。
この理由は、一般的な意味で、列挙型は複数回列挙できるのに対し、列挙型はそれ自体が「複数回」の1つにすぎないためです。
まず、処理しているコレクションの種類を把握して、元の列挙子の残りの部分の上に適切な列挙子を返すことができます。あなたが行く理由。
または...列挙子の残りを新しいコレクションにキャッシュして、それを返すことができます。もちろん、これは元の列挙子を消費しますが、それが何であれ、時間やメモリの点で高価になる可能性があります。
または...いくつかの提案を実行できますが、実際には列挙子を返さないでください。代わりに、列挙可能なクラスのSkipメソッドとTakeメソッドを使用して、必要なものを返します。これにより、新しい列挙可能オブジェクトが返されます。列挙されるたびに、元の列挙可能オブジェクトが列挙され、最初の2つの項目がスキップされ、残りが生成されます。
その最後の段落を言い換えさせてください。残りの部分を新しい列挙可能なものとして返そうとせず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