実装を非表示にすることは、OOPの基本原則であり、すべてのパラダイムで優れたアイデアですが、遅延イテレーションをサポートする言語のイテレーター(または特定の言語で呼び出されるもの)にとって特に重要です。
具体的なタイプのイテラブルを公開する際の問題、またはインターフェイスなどもIList<T>
、それらを公開するオブジェクトではなく、それらを使用するメソッドにあります。たとえば、Foo
sのリストを印刷する関数があるとします。
void PrintFoos(IList<Foo> foos)
{
foreach (foo in foos)
{
Console.WriteLine(foo);
}
}
この関数はfooのリストを印刷するためにのみ使用できます-ただし、それらが実装している場合のみ IList<Foo>
IList<Foo> foos = //.....
PrintFoos(foos);
しかし、リストのすべての偶数インデックス項目を印刷したい場合はどうでしょうか?新しいリストを作成する必要があります。
IList<Foo> everySecondFoo = new List<T>();
bool isIndexEven = true;
foreach (foo; foos)
{
if (isIndexEven)
{
everySecondFoo.Add(foo);
}
isIndexEven = !isIndexEven;
}
PrintFoos(everySecondFoo);
これは非常に長いですが、LINQを使用すると、実際には読みやすいワンライナーで実行できます。
PrintFoos(foos.Where((foo, i) => i % 2 == 0).ToList());
さて、.ToList()
最後に気づきましたか?これにより、遅延クエリがリストに変換されるため、に渡すことができPrintFoos
ます。これには、2番目のリストの割り当てと、アイテムの2つのパス(2番目のリストを作成するための最初のリスト上のパスと、それを印刷するための2番目のリスト上のパス)が必要です。また、これがある場合:
void Print6Foos(IList<Foo> foos)
{
int counter = 0;
foreach (foo in foos)
{
Console.WriteLine(foo);
++ counter;
if (6 < counter)
{
return;
}
}
}
// ........
Print6Foos(foos.Where((foo, i) => i % 2 == 0).ToList());
何foos
千ものエントリがある場合はどうなりますか?それらすべてを調べて、そのうち6つを印刷するためだけに膨大なリストを割り当てる必要があります。
Enter Enumerators-C#のThe Iterator Patternのバージョン。関数がリストを受け入れる代わりに、リストを受け入れEnumerable
ます:
void Print6Foos(Enumerable<Foo> foos)
{
// everything else stays the same
}
// ........
Print6Foos(foos.Where((foo, i) => i % 2 == 0));
これPrint6Foos
で、リストの最初の6項目を遅延して繰り返すことができます。残りの項目を変更する必要はありません。
ここでは、内部表現を公開しないことが重要です。ときにPrint6Foos
何かそのサポートランダムアクセスを- -リストを受け入れ、我々はそれにリストを与えなければならなかったし、署名はそれだけでそれを反復処理するだろうという保証はないので、そのため私たちは、リストを割り当てなければなりませんでした。実装を非表示にするEnumerable
ことで、関数が実際に必要とするもののみをサポートするより効率的なオブジェクトを簡単に作成できます。