linq ソースで go!

39
LINQ ソソソソ GO! IN ソソソ MS ソソソソ 2013/09/21 KOUJI MATSUI (@KEKYO2)

Upload: zia-rodriguez

Post on 02-Jan-2016

47 views

Category:

Documents


0 download

DESCRIPTION

LINQ ソースで GO!. In 名古屋 MS 系秋祭り 2013/09/21 Kouji Matsui (@kekyo2). 自己紹介. けきょ。 会社やってます。 Micoci とまどべん よっかいち。 主に Windows 。 C#, C++/CLI, ATL, C++0x, x86/x64 アセンブラ , WDM, Azure, TFS, OpenCV , Geo, JNI, 鯖管理 , MCP 少々 , 自作 PC, 昔マイコン , 複式簿記経理 最近 は WPF と Prism に足をツッコミ中。. LINQ 知ってますか?. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: LINQ ソースで GO!

LINQ ソースで GO!

IN 名古屋 MS 系秋祭り 2013/09/21KOUJI MATSUI (@KEKYO2)

Page 2: LINQ ソースで GO!

自己紹介 けきょ。 会社やってます。 Micoci とまどべんよっかいち。 主に Windows 。 C#, C++/CLI, ATL, C++0x, x86/x64 アセンブ

ラ , WDM, Azure, TFS, OpenCV, Geo, JNI, 鯖管理 , MCP 少々 , 自作 PC, 昔マイコン , 複式簿記経理

最近は WPF と Prism に足をツッコミ中。

Page 3: LINQ ソースで GO!

LINQ 知ってますか? ビデオチャット製品ではありませんw アイドルグループではありませんww .NET Framework 3.5 (C# 3.0) にて導入された、「統合言語ク

エリ」拡張です。 (Language Integrated Query)

var results =from person in personswhere (person.Age >= 30) && (person.IsFemale == true)orderby person.FirstName, person.LastNameselect person;

foreach (var result in results){

Console.WriteLine(“{0} {1}”, result.FirstName, result.LastName);}

人員一覧の中から、年齢が 30 歳以上、かつ女性の人員を抽出し、名前・苗字の順でソートする

クエリ結果は foreach で列挙可能

Page 4: LINQ ソースで GO!

LINQ はどんな場面で使える? 配列にクエリを掛ける

var persons = new[] { new Person { FirstName=“Kouji”, LastName=“Matsui”, Age=41, IsFemale=false }, new Person { FirstName=“Mogeko”, LastName=“Moge”, Age=35, IsFemale=true }, new Person { FirstName=“Uhyo”, LastName=“Hidebu”, Age=31, IsFemale=true },};

var results =from person in personswhere (person.Age >= 30) && (person.IsFemale == true)orderby person.FirstName, person.LastNameselect person;

Person クラスの配列

配列から抽出する

Page 5: LINQ ソースで GO!

LINQ はどんな場面で使える? リストにクエリを掛ける

var persons = new List<Person>();persons.Add( new Person { FirstName=“Kouji”, LastName=“Matsui”, Age=41, IsFemale=false });persons.Add( new Person { FirstName=“Mogeko”, LastName=“Moge”, Age=35, IsFemale=true });persons.Add( new Person { FirstName=“Uhyo”, LastName=“Hidebu”, Age=31, IsFemale=true });

var results =from person in personswhere (person.Age >= 30) && (person.IsFemale == true)orderby person.FirstName, person.LastNameselect person;

抽出クエリ文は、配列の時と全く同じ

リストに Person を格納

Page 6: LINQ ソースで GO!

LINQ はどんな場面で使える? ジェネリックではないリストは駄目

var persons = new ArrayList();persons.Add( new Person { FirstName=“Kouji”, LastName=“Matsui”, Age=41, IsFemale=false });persons.Add( new Person { FirstName=“Mogeko”, LastName=“Moge”, Age=35, IsFemale=true });persons.Add( new Person { FirstName=“Uhyo”, LastName=“Hidebu”, Age=31, IsFemale=true });

// 構文エラーvar result =

from person in personswhere (person.Age >= 30) && (person.IsFemale == true)orderby person.FirstName, person.LastNameselect person;

error CS1934: ソース型 'System.Collections.ArrayList' のクエリ パターンの実装が見つかりませんでした。 'Where' が見つかりません。範囲変数 'person' の型を明示的に指定してください。

ArrayList に Person を格納

Page 7: LINQ ソースで GO!

「 Where 」って何よ? 「‘ Where’ が見つかりません」… そもそも、その先頭大文字の「 Where 」って何? 実は LINQ のクエリ構文は、メソッド構文に置き換えられてコンパイ

ルされる。// クエリ構文var results =

from person in personswhere (person.Age >= 30) && (person.IsFemale == true)orderby person.FirstName, person.LastNameselect person;

// メソッド構文(コンパイル時にはこのように解釈される)var results = persons.

Where(person => (person.Age >= 30) && (person.IsFemale == true)).OrderBy(person => person.FirstName).ThenBy(person => person.LastName).Select(person => person);

error CS1934: ソース型 'System.Collections.ArrayList' のクエリ パターンの実装が見つかりませんでした。 'Where' が見つかりません。範囲変数 'person' の型を明示的に指定してください。

Page 8: LINQ ソースで GO!

「 Where 」って何よ? ArrayList クラスのドキュメントを確認。

Where メソッドが無い。仕方ないか。

Page 9: LINQ ソースで GO!

「 Where 」って何よ? そりゃ失礼。では正常な List<T> クラスのドキュメントを確認。

やっぱり無いんですけど ( ゚Д ゚ )

Page 10: LINQ ソースで GO!

「 Where 」は拡張メソッド 拡張メソッドは、 C#3.0 にて導入された。 static クラス内の static メソッドの第一引数に「 this 」を修飾する

事で定義できる。// 拡張メソッドの例。クラス名は完全に任意public static class SampleExtensions{

// 文字列を int に変換する拡張メソッドpublic static int ToInt32(this string stringValue){

return int.Parse(stringValue);}

}

public sealed class MainClass{

public static void Main(){

var string123 = “123”;var int123 = string123.ToInt32(); // 拡張メソッドの呼び出し

}}

string 型に対して、「 this 」の修飾

string 型インスタンスメソッドの呼び出しのように見える(書ける)

Page 11: LINQ ソースで GO!

「 Where 」は拡張メソッド Where メソッドは、 System.Linq.Enumerable クラスに定義され

ている。

// System.Linq.Enumerablepublic static class Enumerable{

// Where 拡張メソッド(擬似コード)public static IEnumerable<T> Where<T>(

this IEnumerable<T> enumerable,Func<T, bool> predict)

{foreach (var value in enumerable){

if (predict(value) == true){

yield return value;}

}}

}

IEnumerable<T> インターフェイス型に対して「 this 」の修飾

Page 12: LINQ ソースで GO!

それで? でも、配列やリストは、 IEnumerable<T> 型じゃないよ? .NET の配列は、 IEnumerable<T> インターフェイスを自動的に実装

している。// 配列は、 IEnumerable<T> を実装しているvar persons = new[]

{new Person { FirstName=“Kouji”, LastName=“Matsui” }

};

IEnumerable<Person> personsEnumerable = persons; // OK

List<T> クラスは、 IEnumerable<T> を実装している。// List<T> は、 IEnumerable<T> を実装しているvar persons = new List<Person>();persons.Add(

new Person { FirstName=“Kouji”, LastName=“Matsui” });

IEnumerable<Person> personsEnumerable = persons; // OK

Page 13: LINQ ソースで GO!

IEnumerable<T>

T 型の配列 T[]

それで?

List<T>

IEnumerable<T> Where<T>(this IEnumerable<T> enumerable, …)

つまり、両方とも抽象基底インターフェイスとして、 IEnumerable<T> インターフェイスを実装している。

だから、どちらでも同じWhere拡張メソッドが

使える!!

System.Linq.Enumerable クラス

Page 14: LINQ ソースで GO!

ArrayList は? なぜ ArrayList クラスは駄目なのか? ArrayList クラスが実装しているインターフェイス

は、 IEnumerable<T> ではなく、 IEnumerable インターフェイス。 IEnumerable インターフェイスの Where 拡張メソッドは存在しない。 じゃあ、全く使えないかというと、要するに T 型を特定して、ジェネ

リックな IEnumerable<T> に変換すればいい。// ArraList を用意var persons = new ArrayList();

// ( ArrayList に様々なインスタンスを追加)

// すべての要素をキャスト(キャストに失敗すれば例外がスロー)IEnumerable<Person> persons2 = persons.Cast<Person>(); // OK

// 又は、指定された型のインスタンスだけを抽出IEnumerable<Person> persons3 = persons.TypeOf<Person>(); // OK

Cast ・ TypeOf メソッドは、「 this IEnumerable 」と定義された拡張メソッド。

Page 15: LINQ ソースで GO!

ところで。 クエリの結果を foreach で回してたっけ。

var results =from person in personswhere (person.Age >= 30) && (person.IsFemale == true)orderby person.FirstName, person.LastNameselect person;

foreach (var result in results){

Console.WriteLine(“{0} {1}”, result.FirstName, result.LastName);}

ぐるぐる

foreach で回すことができる条件は?

Page 16: LINQ ソースで GO!

今さら foreach foreach で回すことができるインスタンスは、 IEnumerable イ

ンターフェイスを実装していること。 IEnumerable って言うと、配列とか、リストだっけ…

// 配列var persons = new[] { new Person { FirstName=“Kouji”, LastName=“Matsui”, Age=41, IsFemale=false }, new Person { FirstName=“Mogeko”, LastName=“Moge”, Age=35, IsFemale=true }, new Person { FirstName=“Uhyo”, LastName=“Hidebu”, Age=31, IsFemale=true },};

// 配列を回してみたforeach (var person in persons){

Console.WriteLine(“{0} {1}”, person.FirstName, person.LastName);}

なんだ、 LINQ クエリと一緒じゃん。一緒、なのか??  ( ゚Д ゚ )

IEnumerable<T>

T 型の配列 T[] List<T>

IEnumerable

継承・実装関係

Page 17: LINQ ソースで GO!

配列・リスト・そして LINQクエリ IEnumerable<T> も OK なので、 LINQ クエリは foreach でそのまま

回せる。 「逆に言えば」、 LINQ クエリは IEnumerable<T> インターフェイス

を実装している? と言う事は?

// 実は LINQ クエリの結果は IEnumerable<T>IEnumerable<Person> results =

from person in personswhere (person.Age >= 30) && (person.IsFemale == true)orderby person.FirstName, person.LastNameselect person;

// 前段の LINQ クエリに対して、更に LINQ クエリを適用するIEnumerable<Person> results2 =

from result in resultswhere result.LastName.StartsWith(“Suzuki”) == trueselect result;

更にこの結果に対して LINQ クエリを…

Page 18: LINQ ソースで GO!

効率は? LINQ クエリを数珠つなぎにして、効率悪くないの? 悪いとも言えるし、変わらないともいえる。 where 絞り込み条件を完全に統合できるなら、その方が効率が良い。

// where 条件をまとめるvar results =

from person in personswhere (person.Age >= 30) && (person.IsFemale == true) &&

(result.LastName.StartsWith(“Suzuki”) == true)orderby person.FirstName, person.LastNameselect person;

クエリの意味が変わってしまわないように注意

Page 19: LINQ ソースで GO!

起源を思い出せ 効率が変わらないって? クエリ構文は、メソッド構文に置き換えられてコンパイルされる。

// まとめると、単に連結されただけ。var results2 = persons.

Where(person => (person.Age >= 30) && (person.IsFemale == true)).OrderBy(person => person.FirstName).ThenBy(person => person.LastName).Select(person => person). // ← しいて言えばここが無駄Where(result => result.LastName.StartsWith(“Suzuki”) == true).Select(result => result);

var results = persons.Where(person => (person.Age >= 30) && (person.IsFemale == true)).OrderBy(person => person.FirstName).ThenBy(person => person.LastName).Select(person => person);

var results2 = results.Where(result => result.LastName.StartsWith(“Suzuki”) == true).Select(result => result);

クエリ構文だと、クエリが分割されているだけで効率が悪いように見えるが、実際はそれほどでもない。

Page 20: LINQ ソースで GO!

結局のところ LINQ クエリは、 IEnumerable<T> インターフェイスを返すメ

ソッドを数珠つなぎにしただけ。

// 全てが、 IEnumerable<T> インターフェイスを利用した、拡張メソッド群の呼び出しで解決される。var results2 = persons.

Where(person => (person.Age >= 30) && (person.IsFemale == true)).OrderBy(person => person.FirstName).ThenBy(person => person.LastName).Select(person => person).Where(result => result.LastName.StartsWith(“Suzuki”) == true).Select(result => result);

// System.Linq.Enumerable クラスpublic static IEnumerable<T> Where<T>(this IEnumerable<T> enumerable, …);public static IEnumerable<T> Select<T>(this IEnumerable<T> enumerable, …);public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> enumerable, …);public static IEnumerable<T> ThenBy<T>(this IEnumerable<T> enumerable, …);

これらは全て、 Enumerable クラスに定義されている拡張メソッド群

OrderBy と ThenBy は込み入った理由から本当はこの通りではないが、同じように理解してよい

Page 21: LINQ ソースで GO!

一体、ソースの話はどこにw w 前節までに、 LINQ クエリの肝は「 IEnumerable<T> インター

フェイス」と、その拡張メソッド群であることが明らかになりました。というか、これを強くイメージしてほしかったので、長々と解説しました。

言い換えると、「 IEnumerable<T> インターフェイスのインスタンスを返しさえすれば、フリーダムにやって OK 」って事です。

< マダー?

Page 22: LINQ ソースで GO!

IEnumerable<T> を返す LINQ で使える独自のメソッドを作りたい。例として、「指定さ

れた個数の乱数を返す」 LINQ ソースを考える。

// イケてない実装(配列を作って、乱数を格納して返す)public IEnumerable<int> GetRandomNumbers(int count){

var r = new Random();var results = new int[count];for (var index = 0; index < results.Length; index++){

results[index] = r.Next();}return results; // 配列は IEnumerable<T> を実装しているので OK

}

// こう使えるvar results =

from rn in GetRandomNumbers(1000000) // これって…where (rn % 2) == 0select rn;

個数がデカいとちょっと…

Page 23: LINQ ソースで GO!

オンザフライで乱数を生成(1) 要するに、 IEnumerable<T> で返せばいいのだから、配列やリストでなく

ても良い。 IEnumerable<T> を実装した、独自のクラスを定義する。 IEnumerable<T> は、 IEnumerator<T> のファクトリとなっているので、

これらを実装する。// IEnumerable<int> を実装したクラスを定義internal sealed class RandomNumberEnumerable : IEnumerable<int>{

private readonly int count_; // 個数を記憶する

public RandomNumberIEnumerable(int count){

count_ = count;}

public IEnumerator<int> GetEnumerator(){

// RandomNumberEnumerator を作って返す(ファクトリメソッド)return new RandomNumberEnumerator(count_);

}

IEnumerator IEnumerable.GetEnumerator(){

// 非ジェネリック実装は、単にジェネリック実装を呼び出すreturn this.GetEnumerator();

}}

Page 24: LINQ ソースで GO!

オンザフライで乱数を生成(2) GetEnumerator() は IEnumerator<T> を返す必要があるので、

そのためのクラスを準備。// 乱数生成の本体クラスinternal sealed class RandomNumberEnumerator : IEnumerator<int>{

private readonly Random r_ = new Random();private readonly int count_;private int remains_;

public RandomNumberEnumerator(int count){

count_ = count;remains_ = count;

}

public int Current{

get;private set;

}

// 次があるかどうかを返すpublic bool MoveNext(){

if (remains_ >= 1){

remains_--;this.Current = r.Next(); // 次の値を保持return true;

}return false;

}}

実際には、 Reset メソッドも必要…

Page 25: LINQ ソースで GO!

オンザフライで乱数を生成(3) やっと完成。

// オンザフライ出来た!public IEnumerable<int> GetRandomNumbers(int count){

// RandomNumberEnumerable クラスを生成return new RandomNumberEnumerable(count);

}

// こう使えるvar results =

from rn in GetRandomNumbers(1000000) // メモリを過度に消費しない !!

where (rn % 2) == 0select rn;

め、面倒クサ過ぎる orz

Page 26: LINQ ソースで GO!

yield return C#2.0 にて、「 yield 」予約語が導入された。 これを使うと、 IEnumerable インターフェイスの実装が劇的に簡単に!

(つまり、前節の方法は、 .NET 1.1 までの方法)

// イケてる実装public IEnumerable<int> GetRandomNumbers(int count){

var r = new Random();for (var index = 0; index < count; index++){

yield return r.Next(); // yield return って書くだけ!!}

}

// こう使えるvar results =

from rn in GetRandomNumbers(1000000) // 勿論、メモリ消費しないwhere (rn % 2) == 0select rn;

yield を使うと、コンパイル時に、自動的に前述のような内部クラスが生成される(ステートマシンの生成)

Page 27: LINQ ソースで GO!

yield を使って、拡張メソッド 任意数の IEnumerable<T> インスタンスを結合する拡張メソッドを作る。

// 適当な static クラスに定義public static IEnumerable<T> Concats<T>(

this IEnumerable<T> enumerable,params IEnumerable<T>[] rhss)

{// まず、自分を全て列挙foreach (var value in enumerable){

yield return value;}

// 可変引数群を列挙foreach (var rhs in rhss){

// 個々の引数を列挙foreach (var value in rhs){

yield return value;}

}}

可変引数群を受け取る

Page 28: LINQ ソースで GO!

yield を使って、拡張メソッド// 配列

var persons1 = new[] { new Person { FirstName=“Kouji”, LastName=“Matsui”, Age=41, IsFemale=false }, new Person { FirstName=“Mogeko”, LastName=“Moge”, Age=35, IsFemale=true }, new Person { FirstName=“Uhyo”, LastName=“Hidebu”, Age=31, IsFemale=true }, };

// リストvar persons2 = new List<Person>();persons2.Add(new Person { FirstName=“Kouji”, LastName=“Matsui”, Age=41, IsFemale=false });persons2.Add(new Person { FirstName=“Mogeko”, LastName=“Moge”, Age=35, IsFemale=true });persons2.Add(new Person { FirstName=“Uhyo”, LastName=“Hidebu”, Age=31, IsFemale=true });

// 何らかの LINQ クエリvar persons3 =

from person in personsXwhere (person.Age >= 30) && (person.IsFemale == true)select person;

// 全部結合var results = persons1.Concats(persons2, persons3);

この部分が可変引数群( rhss )

Page 29: LINQ ソースで GO!

yield でこんな事も可能 yield によって勝手にステートマシンが作られるので、逆手にとって…

// 移動角度群を返す LINQ ソースpublic static IEnumerable<double> EnemyAngles(){

// 敵の移動角度を以下のシーケンスで返すyield return 0.0;yield return 32.0;yield return 248.0;yield return 125.0;yield return 66.0;yield return 321.0;

// 10 ステップはランダムな方角に移動var r = new Random();for (var index = 0; index < 10; index++){

yield return r.Next(360);}

// 最後に少し動いて死亡yield return 37.0;yield return 164.0;

}

foreach で回せば、これらの順で値が取得出来る。もちろん、 LINQ クエリで値を加工することも可能

重要なのは、 yield を使う事で、返却する値を自由自在にコントロールできると言う事単独で値を返したり、ループさせたり、それらを組み合わせたりも OK

Page 30: LINQ ソースで GO!

必ず IEnumerable<T> ? LINQ ソースとなるためには、必ず IEnumerable<T> を返さな

ければならないのか?

// 例えば、パラレル LINQ クエリvar results =

from person in persons.AsParallel()where (person.Age >= 30) && (person.IsFemale == true)orderby person.FirstName, person.LastNameselect person;

// メソッド構文var results = persons.

AsParallel().Where(person => (person.Age >= 30) && (person.IsFemale == true)).OrderBy(person => person.FirstName).ThenBy(person => person.LastName).Select(person => person);

AsParallel するだけで、あとは普通のLINQ と変わらないよ?

Page 31: LINQ ソースで GO!

必ず IEnumerable<T> ? パラレル LINQ クエリの結果は、実は ParallelQuery<T> 型。

// 似ているようで、違うのか?ParallelQuery<T> results = persons.

AsParallel().Where(person => (person.Age >= 30) && (person.IsFemale == true)).OrderBy(person => person.FirstName).ThenBy(person => person.LastName).Select(person => person);

Page 32: LINQ ソースで GO!

必ず IEnumerable<T> ? ParallelQuery<T> クラスは、 IEnumerable<T> インターフェイスを

実装している。 じゃあ、 Where 拡張メソッドの呼び出しは、結局同じってこと??

IEnumerable<T>

ParallelQuery<T>

IEnumerable<T> Where<T>(this IEnumerable<T> enumerable, …)

System.Linq.Enumerable クラス

Page 33: LINQ ソースで GO!

ParallelEnumerable クラス ParallelQuery<T> クラスに対応する Where 拡張メソッドは、

Enumerable クラスではなく、 ParallelEnumerable クラスに定義されている。

C# コンパイラは、型がより一致する拡張メソッドを自動的に選択するため、 ParallelQuery<T> に対して Where を呼び出すと、ParallelEnumerable.Where が呼び出される。

IEnumerable<T> Where<T>(this IEnumerable<T> enumerable, …)

ParallelQuery<T> Where<T>(this ParallelQuery<T> enumerable, …)

IEnumerable<T>

ParallelQuery<T>

System.Linq.Enumerable クラス

System.Linq.ParallelEnumerable クラス

ParallelQuery<T> の場合は、こっちのWhere が呼び出される。この実装がパラレルLINQ を実現する。

Page 34: LINQ ソースで GO!

わざと似せている ParallelEnumerable クラスには、 Enumerable クラスに定義され

ているメソッドと同じシグネチャ(但し、 IEnumerable<T> → ParallelQuery<T> )の、全く異なる実装が定義されている。

// System.Linq.ParallelEnumerable クラスpublic static ParallelQuery<T> AsParallel<T>(this IEnumerable<T> enumerable);public static ParallelQuery<T> Where<T>(this ParallelQuery<T> enumerable, …);public static ParallelQuery<T> Select<T>(this ParallelQuery<T> enumerable, …);public static ParallelQuery<T> OrderBy<T>(this ParallelQuery<T> enumerable, …);public static ParallelQuery<T> ThenBy<T>(this ParallelQuery<T> enumerable, …);

戻り値の型も、 ParallelQuery<T> となっているので、

// メソッドの連結ParallelQuery<T> results = persons.

AsParallel().Where(person => (person.Age >= 30) && (person.IsFemale == true)).OrderBy(person => person.FirstName).ThenBy(person => person.LastName).Select(person => person;

これらの戻り値の型は、すべからくParallelQuery<T> 型。だから、全て ParallelEnumerable の実装が使われる。→これによって、クエリのパラレル実行が行われる。

Page 35: LINQ ソースで GO!

まだある、似て異なる実装 LINQ to SQL や LINQ to Entities のデータベースコンテキスト

から LINQ クエリを記述すると、 IEnumerable<T> ではなく、IQueryable<T> が返される。

// LINQ to Entities に対して、 LINQ クエリを記述するvar results =

from person in personsContext // DB コンテキストがソースwhere (person.Age >= 30) && (person.IsFemale == true)orderby person.FirstName, person.LastNameselect person;

// メソッド構文と戻り値の型IQueryable<Person> results = personsContext.

Where(person => (person.Age >= 30) && (person.IsFemale == true)).OrderBy(person => person.FirstName).ThenBy(person => person.LastName).Select(person => person);

IQueryable<T>

Page 36: LINQ ソースで GO!

まだある、似て異なる実装 IQueryable<T> に対応する Where 拡張メソッドは、 Enumerable ク

ラスではなく、 Queryable クラスに定義されている。 考え方はパラレル LINQ と同じ。

IEnumerable<T> Where<T>(this IEnumerable<T> enumerable, …)

IQueryable<T> Where<T>(this IQueryable<T> queryable, …)

IEnumerable<T>

IQueryable<T>

System.Linq.Enumerable クラス

System.Linq.Queryable クラス

データベースに WHERE句を送信するための仕掛けを持った実装。

Page 37: LINQ ソースで GO!

やっぱり LINQ ソースは IEnumerable<T> を継承したクラスやインターフェイスを使うのか? ReactiveExtension ライブラリが最後の常識を覆す。

// マウス移動イベント発生時に座標をフィルタするIObservable<Point> rx =

Observable.FromEvent<MouseEventArgs>(window, “MouseMove”).Select(ev => ev.EventArgs.GetPosition(window)).Where(pos => (pos.X < 100) && (pos.Y < 100));

// rx の条件が満たされたときに実行する内容を記述rx.Subscribe(pos =>

{window.Text = string.Format(“{0}, {1}”, pos.X, pos.Y);

});

IObservable<T> は、 IEnumerable<T> と全く関係がない。しかし、 Where や Select 拡張メソッドを用意する事で、まるで LINQ クエリのように見えるようにしている。

Page 38: LINQ ソースで GO!

まとめ ベーシックな LINQ クエリは、すべからく IEnumerable<T> を使

用する。その場合、 Enumerable クラスに定義された拡張メソッドを使用して、 LINQ の機能を実現している。

独自の拡張メソッドを定義すれば、 LINQ 演算子を追加できる。 独自の LINQ ソースを作るなら、 yield 構文を使うと良い。 拡張された LINQ クエリ(パラレル LINQ や LINQ to Entities な

ど)は、 IEnumerable<T> を継承した、新たなクラスやインターフェイスを使用して、拡張メソッドを切り替えさせる事で、似て異なる動作を実現する。これにより、既存の拡張メソッドの動作に影響を与えることなく、かつ、容易に理解可能な API を生み出すことができる。

IEnumerable<T> と全く関係のない型を使用したとしても、まるでLINQ クエリのように見せることができる。これを「 Fluent API 」パターンと呼ぶ。Fluent API を用意すれば、 LINQ クエリのようなフレンドリ感と、VS上でのサクサクタイピングが実現する。

Page 39: LINQ ソースで GO!

ご静聴ありがとうございました m(_ _)m