続・はじめての LINQ

この記事は前回の「[はじめての LINQ](http://qiita.com/nskydiving/items/c9c47c1e48ea365f8995)」の補足的な内容になっています。

まだ前回の記事を読んでいない人は、先にそちらから読むことをおすすめします。

# LINQ の使い方
前回の「[はじめての LINQ](http://qiita.com/nskydiving/items/c9c47c1e48ea365f8995)」では紹介しきれなかったメソッドをいくつか見ていきましょう。

### ・射影
```c#
using System;
using System.Linq;

namespace LinqTest
{
	class MainClass
	{
		public static void Main(string[] args)
		{
			// Fruit クラスは定義せず、匿名クラスを使用する
			var fruitList = new[] {
				new  {Name = "りんご", Price = 300},
				new  {Name = "バナナ", Price = 300},
				new  {Name = "パイナップル", Price = 1000},
				new  {Name = "いちご", Price = 500},
			};

			// 各要素を射影する
			var selectList = fruitList.Select(x => x.Name);
			Console.WriteLine("=== selectList ===");
			foreach (var x in selectList)
			{
				Console.WriteLine(x);
			}

			// 各要素を複数の要素に平坦化して射影する
			var selectManyList = fruitList.SelectMany(x => x.Name.ToCharArray());
			Console.WriteLine("=== selectManyList ===");
			foreach (var x in selectManyList)
			{
				Console.WriteLine(x);
			}

			// 各要素をグループ化する
			var groupByList = fruitList.GroupBy(x => x.Price);
			Console.WriteLine("=== groupByList ===");
			foreach (var g in groupByList)
			{
				Console.WriteLine("Key: " + g.Key);
				foreach (var x in g)
				{
					Console.WriteLine(" " + x);
				}
			}
		}
	}
}
```

[コンソール出力結果]

```
=== selectList ===
りんご
バナナ
パイナップル
いちご
=== selectManyList ===
り
ん
ご
バ
ナ
ナ
パ
イ
ナ
ッ
プ
ル
い
ち
ご
=== groupByList ===
Key: 300
 { Name = りんご, Price = 300 }
 { Name = バナナ, Price = 300 }
Key: 1000
 { Name = パイナップル, Price = 1000 }
Key: 500
 { Name = いちご, Price = 500 }
```

### ・結合
```c#
using System;
using System.Linq;

namespace LinqTest
{
	class MainClass
	{
		public static void Main(string[] args)
		{
			// Fruit クラスは定義せず、匿名クラスを使用する
			var fruitList1 = new[] {
				new  {Name = "りんご", Price = 300},
				new  {Name = "バナナ", Price = 300},
				new  {Name = "パイナップル", Price = 1000},
				new  {Name = "いちご", Price = 500},
			};

			var fruitList2 = new[] {
				new  {Name = "りんご", Stock = 100},
				new  {Name = "バナナ", Stock = 50},
				new  {Name = "パイナップル", Stock = 40},
			};

			var fruitList3 = new[] {
				new  {Name = "ぶどう", Price = 700},
				new  {Name = "もも", Price = 600},
			};

			// 内部結合
			var joinList = fruitList1.Join(
				fruitList2,
				outer => outer.Name,
				inner => inner.Name,
				(outer, inner) => new {
					outer.Name,
					outer.Price,
					inner.Stock
				});
			Console.WriteLine("=== joinList ===");
			foreach (var x in joinList)
			{
				Console.WriteLine(x);
			}

			// 外部結合
			var groupJoinList = fruitList1.GroupJoin(
				fruitList2,
				outer => outer.Name,
				inner => inner.Name,
				(outer, inner) => new {
					outer.Name,
					outer.Price,
					Stock = inner.Select(x => x.Stock).Sum()
				});
			Console.WriteLine("=== groupJoinList ===");
			foreach (var x in groupJoinList)
			{
				Console.WriteLine(x);
			}

			// 連結
			var concatList = fruitList1.Concat(fruitList3);
			Console.WriteLine("=== concatList ===");
			foreach (var x in concatList)
			{
				Console.WriteLine(x);
			}
		}
	}
}
```

[コンソール出力結果]

```
=== joinList ===
{ Name = りんご, Price = 300, Stock = 100 }
{ Name = バナナ, Price = 300, Stock = 50 }
{ Name = パイナップル, Price = 1000, Stock = 40 }
=== groupJoinList ===
{ Name = りんご, Price = 300, Stock = 100 }
{ Name = バナナ, Price = 300, Stock = 50 }
{ Name = パイナップル, Price = 1000, Stock = 40 }
{ Name = いちご, Price = 500, Stock = 0 }
=== concatList ===
{ Name = りんご, Price = 300 }
{ Name = バナナ, Price = 300 }
{ Name = パイナップル, Price = 1000 }
{ Name = いちご, Price = 500 }
{ Name = ぶどう, Price = 700 }
{ Name = もも, Price = 600 }
```

# 遅延評価
LINQ は[遅延評価](https://ja.wikipedia.org/wiki/%E9%81%85%E5%BB%B6%E8%A9%95%E4%BE%A1)されます。
と、いきなりそう言われてもよく分からないと思いますので、まずは以下のコードを実行してみてください。
また、参考までに私の実行環境でのコンソール出力結果も載せておきます。

```c#
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
      
namespace LinqTest
{
	class MainClass
	{
		public static void Main(string[] args)
		{
			// LINQ 処理
			var stopWatch1 = new Stopwatch();
			stopWatch1.Start();
			var list = Enumerable.Range(1, 100);
			var resultList = list
			                 .Select(x => HeavyFunc(x));
			stopWatch1.Stop();
			Console.WriteLine("stopWatch1 TotalSeconds: " + stopWatch1.Elapsed.TotalSeconds);

			// コンソール出力処理
			var stopWatch2 = new Stopwatch();
			stopWatch2.Start();
			foreach (var x in resultList)
			{
				Console.Write(x + " ");
			}
			Console.WriteLine();
			stopWatch2.Stop();
			Console.WriteLine("stopWatch2 TotalSeconds: " + stopWatch2.Elapsed.TotalSeconds);

			Console.WriteLine();
			Console.ReadLine();
		}

		static int HeavyFunc(int number)
		{
			Thread.Sleep(100); // 時間のかかる処理を想定
			return number;
		}
	}
}
```

[コンソール出力結果]

```
stopWatch1 TotalSeconds: 0.0027506
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 
stopWatch2 TotalSeconds: 10.2689368
```

このコードでは 「LINQ 処理」と「コンソール出力処理」の ２ つのブロックに分けて、それぞれの処理に掛かった時間を計測しています。

「LINQ 処理」では、処理の重い HeavyFunc メソッドを 100 回実行しています。
また、「コンソール出力処理」では、その「LINQ 処理」の結果をコンソールに出力しているだけです。

ここで注目してほしいのは、処理が重いはずの「LINQ 処理」が僅か 0.002 秒しか掛かっていないのに、単純な処理しかしていないはずの「コンソール出力処理」には 10 秒も掛かっています。

なぜこのような結果になっているかというと **LINQ は結果が必要になるまで処理を保留**しているからです。
LINQ は、このようにして無駄な処理は行わないようになっています。

この処理が最適化されるということは[遅延評価](https://ja.wikipedia.org/wiki/%E9%81%85%E5%BB%B6%E8%A9%95%E4%BE%A1)の大きなメリットです。
しかし、慣れていないと、いつ処理が実行されているのか分かりづらく、デバッグがやりづらいというデメリットもあります。

# PLINQ
LINQ を並列実行できる [PLINQ](https://msdn.microsoft.com/ja-jp/library/dd460688%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396) という機能があります。

使い方は LINQ のメソッドチェーンの中に AsParallel メソッドを追加するだけです。
簡単ですね。

```c#
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
      
namespace LinqTest
{
	class MainClass
	{
		public static void Main(string[] args)
		{
			// LINQ 処理
			var stopWatch1 = new Stopwatch();
			stopWatch1.Start();
			var list = Enumerable.Range(1, 100);
			var resultList = list
			                 .AsParallel() // 並列実行させる
			                 .Select(x => HeavyFunc(x));
			stopWatch1.Stop();
			Console.WriteLine("stopWatch1 TotalSeconds: " + stopWatch1.Elapsed.TotalSeconds);

			// コンソール出力処理
			var stopWatch2 = new Stopwatch();
			stopWatch2.Start();
			foreach (var x in resultList)
			{
				Console.Write(x + " ");
			}
			Console.WriteLine();
			stopWatch2.Stop();
			Console.WriteLine("stopWatch2 TotalSeconds: " + stopWatch2.Elapsed.TotalSeconds);

			Console.WriteLine();
			Console.ReadLine();
		}

		static int HeavyFunc(int number)
		{
			Thread.Sleep(100); // 時間のかかる処理を想定
			return number;
		}
	}
}
```

[コンソール出力結果]

```
stopWatch1 TotalSeconds: 0.0048343
Thread started: <Thread Pool> #2
Thread started: <Thread Pool> #3
Thread started: <Thread Pool> #4
Thread started: <Thread Pool> #5
Thread started: <Thread Pool> #6
Thread started: <Thread Pool> #7
1 4 2 7 5 8 11 9 12 16 13 15 18 17 19 22 21 23 28 25 26 30 29 31 39 37 33 40 38 34 45 47 41 46 48 42 55 53 49 56 54 50 63 61 59 64 62 60 69 71 67 70 72 68 77 79 75 78 80 76 87 85 81 88 86 82 93 95 89 94 96 90 3 6 10 14 20 24 27 32 35 36 43 44 51 52 57 58 65 66 73 74 83 84 91 92 97 98 99 100 
stopWatch2 TotalSeconds: 2.8799779
```

このコードは、先ほどの[遅延評価](https://ja.wikipedia.org/wiki/%E9%81%85%E5%BB%B6%E8%A9%95%E4%BE%A1)のサンプルコードを並列化したものですが、処理に掛かった時間は先ほどの 3 分の 1 以下になっています。

ただし、並列化したからといって、いつも速くなるとは限りませんので注意してください。


# いろいろな LINQ
今まで紹介してきた配列やコレクションに対して処理を行う LINQ は「LINQ to Objects」と呼ばれています。

それ以外にもいろいろなデータソースに対して処理行う LINQ ライブラリがあります。

[LINQ to Objects](https://msdn.microsoft.com/ja-jp/library/bb397676.aspx)
[LINQ to XML](https://msdn.microsoft.com/ja-jp/library/bb387098(v=vs.110).aspx)
[LINQ to JSON](http://www.newtonsoft.com/json/help/html/LINQtoJSON.htm)
[LINQ to DataSet](https://msdn.microsoft.com/ja-jp/library/bb552415(v=vs.110).aspx)
[LINQ to SQL](https://msdn.microsoft.com/ja-jp/library/bb386976(v=vs.110).aspx)
[LINQ to Entities](https://msdn.microsoft.com/ja-jp/library/bb386964(v=vs.110).aspx)
[LINQ to GameObject](https://github.com/neuecc/LINQ-to-GameObject-for-Unity)
[LINQ to BigQuery](https://github.com/neuecc/LINQ-to-BigQuery)

# RX（Reactive Extensions）
[RX（Reactive Extensions）](https://github.com/Reactive-Extensions/Rx.NET)は一言で言うと **LINQ に「非同期」と「イベント」の要素を加えて拡張したライブラリ**です。

この記事では詳しく紹介しませんので、以下のリンクを参考にしてください。

＠IT＞連載：Reactive Extensions（Rx）入門
http://www.atmarkit.co.jp/fdotnet/introrx/index/index.html

かずきのBlog@hatena＞Reactive Extensionsのはじめかた
http://blog.okazuki.jp/entry/2015/03/23/203825

xin9le.net＞Rx入門
http://blog.xin9le.net/entry/rx-intro