この間、LINQはメソッドの呼び出しに展開されるから、同名のメソッドを用意しておけば、それが呼ばれるようになるよ!みたいなことを書いた。
もう一回書いてみようと思う。
using System; using System.Collections.Generic; using System.Text; // using System.Linq; 自前Selectとか使うためusingしない namespace MyLinq { class Program { static void Main(string[] args) { var values = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; // 偶数だけ取り出して二乗する var results = from value in values where value % 2 == 0 select value * value; foreach (var result in results) { Console.WriteLine(result); } } } // 自前のWhereメソッドとSelectメソッド static class MyExtensions { public static IEnumerable<T> Where<T>(this IEnumerable<T> e, System.Linq.Func<T, bool> f) { Console.WriteLine("俺様Where!!"); ICollection<T> ret = new LinkedList<T>(); foreach (var i in e) { if (f(i)) { ret.Add(i); } } return ret; } public static IEnumerable<U> Select<T, U>(this IEnumerable<T> e, System.Linq.Func<T, U> f) { Console.WriteLine("俺様Select!!"); ICollection<U> ret = new LinkedList<U>(); foreach (var i in e) { ret.Add(f(i)); } return ret; } } }
実行すると↓のようになる
俺様Where!! 俺様Select!! 4 16 36 64 100
ここまではOK。
さて、ちょっと大変なのはここから。
こんなEnumerableを作ってみました。
// 無限に数字を返すEnumerable class InfEnumerable : IEnumerable<int> { public IEnumerator<int> GetEnumerator() { return new InfEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } class InfEnumerator : IEnumerator<int> { private int value = 0; public int Current { get { return value; } } public void Dispose() { } object System.Collections.IEnumerator.Current { get { return Current; } } public bool MoveNext() { value++; return true; } public void Reset() { value = 0; } } }
0,1,2,3,4,5,6.......とintが許す限り数字をカウントアップしていく輩です。
こいつに対してLINQで問い合わせをしてみると…
本物のSystem.Linqを使う
class Program { static void Main(string[] args) { // 最初の10個の偶数を二乗した結果を表示する var values = new InfEnumerable(); var results = from value in values where value % 2 == 0 select value * value; int count = 0; foreach (var result in results) { Console.WriteLine(result); if (count++ >= 10) { break; } } } }
実行するとちゃんと結果が出ます。
4 16 36 64 100 144 196 256 324 400 484
これを、俺様SelectとWhereを使うように置き換えると…
俺様Where!!
の時点でとまっちゃいます。
そりゃそうだ、無限ループしてますからね。
ってことで、無限ループのせいでパソコンが5分くらい固まっちゃいました。
やっと復活です!!
無限ループいくない!!
さて、結局のところ↓のようにイテレータを使うと丸くおさまります。
// 自前のWhereメソッドとSelectメソッド static class MyExtensions { public static IEnumerable<T> Where<T>(this IEnumerable<T> e, System.Linq.Func<T, bool> f) { foreach (var i in e) { if (f(i)) { yield return i; } } } public static IEnumerable<U> Select<T, U>(this IEnumerable<T> e, System.Linq.Func<T, U> f) { foreach (var i in e) { yield return f(i); } } }
これで実行すると、System.Linqに用意されてるSelectやWhereと同じ結果になります。
yield returnが無かったら書くのメンドクサソウだな…
ちなみに、yield returnを使えばInfEnumerableもこんなにスッキリ。
// 無限に数字を返すEnumerable class InfEnumerable : IEnumerable<int> { public IEnumerator<int> GetEnumerator() { for (var i = 0; ; i++) { yield return i; } } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } }