何をするのか
みんな大好きforeachでくるくる回すのに必要なコトって何なの?と言うことで一つ
foreachに必要なこと
一般的には、foreach ( local-variable-type identifier in expression ) のexpressionの部分がIenumerable<T>を持ってるコトが必要と思われがちです。。。が、実はこれだけじゃ無かったりする。
実際、下記のサンプルは動くし、言語仕様的にも規約通りだったりする。
using System;
namespace ForEachSample
{
public class NotEnumerator
{
private readonly int _limit;
public NotEnumerator(int limit)
{
_limit = limit;
Current = -1;
}
public bool MoveNext() => ++Current < _limit;
public int Current { get; set; }
}
public class NotEnumerable
{
public NotEnumerator GetEnumerator()=>new NotEnumerator(10);
}
static class MainEntry
{
static void Main()
{
NotEnumerable value=new NotEnumerable();
foreach (int i in value)
{
Console.WriteLine(i);
}
}
}
}
このように、bool MoveNext()メソッド及びCurrentプロパティを持っている列挙子を返すようなGetEnumerator()メソッドを実装していればforeachで列挙可能だったりする。有り体に言えば、ダックタイピングが可能だよと言うことで。
厳密な定義に関しては、言語仕様の8.8.4 foreach ステートメントの後半に書いてあるので興味のある方はご一読頂ければと。
但しLINQおめーはダメだ
C#2.0くらいまでなら、割とコレなら何でも行けて便利デスネで終わるのですが、時代はC#6.0なので、IEnumerable<T>持っていないと実は結構致命的な問題が出てくる。そう、みんな大好きLINQ1が使えないのだ。
これは、IEnumerable<T>に対する拡張メソッドとして定義されているので、IEnumerable<T>じゃないオブジェクトには利用不可であるという当たり前っちゃ当たり前の理由。
まとめ
原則、IEnumerable<T>を使うが一番良いとは思います。それにイマドキならyieldからそれこそ、Enumerator部分を文字通りyieldするので、この辺気にすることも無いかなと。
じゃあ全然使いどころが無いかと言われると、そんなことも無いのが面白いところで、特定の状況でこいつ使うと、素のyield使った実装よりシャア並に早くなったりすることもある。
その辺は又別のエントリでまとめてみようかと。
と言うことでおつきあい頂ありがとうございました。
-
正確に言えばLINQ to Objectデスね。 ↩