渋谷ほととぎす通信

完全趣味でやってるUnityメモ。説明できないところを説明できるようにするための個人ブログ。昨日の自分より少しでも大きくなれるように。。。 ※所属団体とは一切関係がありません

初心者LINQerがLINQと戯れる : UniRx強化月間


UniRXを勉強する前にLINQについて軽く知識を蓄えておこうと思い、こちらの本のLINQ項目を読みながら勉強を進めていきます。
※分からないキーワードも調べながら行きます。

プログラミングC# 第7版

プログラミングC# 第7版

そもそもLINQとは

読み方はリンクで、Language INtegrated Query の略です。

統合言語クエリ (LINQ) は、Visual Studio 2008 および .NET Framework Version 3.5 で導入された革新的な機能で、オブジェクトの世界とデータの世界の間の橋渡しをするものです。

LINQ の概要より引用

LINQ以前のクエリは文字列だったので自動補完(インテリセンス)が使えませんでした。また、様々な場面に応じてクエリを覚えてないといけないという問題点をLINQは解決しています。

LINQC#VisualBasicの言語なのでコード補完され、使い慣れた演算子を使うことができ、クエリの敷居を下げています。

こちらの本家の記事がC#におけるLINQの概要を解説しています。
C# の LINQ の概要

簡単にLINQの流れを説明するとこんな感じです。

  1. データソースを取得する
  2. クエリを作成する
  3. クエリを実行する
  4. 結果を得る

「クエリ = 問い合わせ」です。 取得したデータに何らかの条件で問合せ、その結果を得ます。

例えば、{1, 2, 3, 4}という配列 == データソースに対し、偶数だけを取得したい == クエリを実行すると、{2, 4} == 結果を得ます。

LINQプロバイダ

本書でよく出てくるLINQプロバイダというものををまず明らかにしておきます。

LINQでは、問い合わせ先であるデータソース(「from n in XXX」のXXXの部分。これについてはすぐ次で解説しています)の型に基づいて、クエリがどのようにコンパイルされ、実行されるのかが決まる仕組みになっています。端的にいえば、最終的に問い合わせ処理の実行を担当するクラス(通常はクラス群)がクエリ対象の種類によって異なるというわけです。このようなデータソースの種類ごとに存在する一連のクラス群は「LINQプロバイダ」と呼ばれます。  

http://image.itmedia.co.jp/ait/articles/0803/25/dt-cslinq01_02.gif

特集:C#プログラマーのためのLINQ超入門(前編):LINQ(リンク)の基礎知識 (3/4) - @ITより引用

要するにLINQプロバイダLINQ to ObjectsLINQ to SQLなどのクラス群のことです。

LINQ to Objectsとは

UnityでLINQを使いたいため、LINQ to Objectsに絞ってサンプルを例に解説していく前に、そもそもLINQプロバイダLINQ to Objectsとは何かを調べてみます。

"LINQ to Objects" という用語は、LINQ to SQL [LINQ to SQL] や LINQ to XML などの中間 LINQ プロバイダーまたは API を使用せずに、LINQ クエリを任意の IEnumerable コレクションまたは IEnumerable コレクションと直接組み合わせて使用することを意味します。 LINQ を使用して、List、Array、または Dictionary などの任意の列挙可能なコレクションを照会できます。 このコレクションは、ユーザー定義のコレクションでも、.NET Framework API から返されたコレクションでもかまいません。

LINQ to Objectsより引用

こちらも本家サイトの情報ですが、要は、

LINQ to ObjectsとはIEnumerableコレクションを使用するということです。

IEnumerableとは

IEnumerable は 列挙できるすべての非ジェネリックコレクションの基本インターフェイスです。

IEnumerable インターフェイス (System.Collections)より引用

IEnumerableインターフェイスは、リストや配列を使用するために必要なインターフェイスです。これがないとfor文などの反復処理ができません。Listや配列ではなく、カスタムな反復処理をしたい場合は、IEnumerableインターフェイスを実装することで可能になります。

また、IEnumerableインターフェイスは配列、リスト、Dicionaryにはもともと実装されているインターフェイスです。

ここまでのまとめ

LINQ to Objectsはコレクションに対してクエリを実行するということになります。

ここからは実際にLINQ to Objectsを使ったサンプルです。ソースを読むことで、先の理解し難い説明が把握できていくと思います。


こちらのint型配列をLINQで色々してみます。

int[] nums = { 1, -2, 3, 4, 5, 6, 7 };

例1. 3より大きい値の配列を結果として受け取る

このような出力にする↓↓

4, 5, 6, 7

LINQは2つの書き方があります。

  • クエリ式
  • クエリ式を展開したもの(別名求む)

クエリ式

  1. from句でデータソースを指定
  2. where句でクエリを作成
  3. select句で結果を返す
IEnumerable<int> result = 
    from n in nums
    where n > 3
    select n;

クエリ式を展開
※クエリ式を展開した形で書くとクエリのfrom句が消えます

クエリ式の各句がそのまま関数になっていて、引数がラムダ式になっているのが特徴です。

個人的にはIEnumerableコレクション (ここで言うnums変数) に直接クエリ関数を実行できコード量が減るので、こちらの書き方の方が好きです。

IEnumerable<int> result = nums
    .Where (n => n > 3)
    .Select (n => n);

以下、クエリ式を展開した形で説明していきます。

上記のようなそのまま値を出力する場合はSelect関数は省略可能

IEnumerable<int> result = 
    .Where (n => n > 3);

一般的にLINQクエリの結果を保存する変数はvarキーワードを使うことが一般的とのこと。(しかし時と場合によっては型を明示的に指定します)

var result = nums
    .Where (n => n > 3);

例2. 3より大きい且つ、2で割り切れる値の配列を結果として受け取る

このような出力にする↓↓

4, 6

Where関数内の条件を変更

var result = nums
    .Where (n => n > 3 && n % 2 == 0);

または、Whereを複数使って条件を追加

var result = nums
    .Where (n => n > 3)
    .Where (n => n % 2 == 0);

例2. 2番目以降の要素が対象且つ、3より大きい且つ、2で割り切れる値の配列を結果として受け取る

var result = nums
    .Where ((n, index) => index > 2 &&  n > 3 && n % 2 == 0);

例3. 3より大きい配列の各要素に文字列"piyo"を追加した配列を受け取る

このような出力にする↓↓

"4piyo", "5piyo", "6piyo", "7piyo"

Select関数で加工する

var result = nums
    .Where (n => n > 3)
    .Select (n => n.ToString () + "piyo");

例4. 異なる型が混在する配列からstring型だけを抽出した配列を受け取る

今回使用するobject型の配列

IEnumerable<object> mix = new object[] {
    "hoge", "piyo", 123, "foo", 456
};

このような出力にする↓↓

"hoge", "piyo", "foo"

OfType関数を使用

var result = mix
    .OfType<string> ();

例5. 匿名型で受け取る

このサンプルの場合は、変数prはvarキーワードを使わざるを得ません。

var pr = nums
    .Where (n => n > 5)
    .Select (n => new {n});

まとめ

このように、簡単にコレクションの要素を指定した状態で取り出すことができます。 わざわざforeach文とif文を駆使して読みづらいソースを書く必要はLINQによってなくなりました。 本記事は本当に基本的なことをまとめています。これからUniRxを勉強していく中で新しいLINQの使い方など出てくると思うので、そちらもまた別記事でまとめていこうと思います。

注意

ただUnity上でLINQを使う際は注意が必要で、UnityEditorやAndroid実機では動くけどiOS実機で例外が発生する場合があります。以下参考。
qiita.com

参考

Chapter15 LINQとクエリ式 − @IT
この特集はありがたいです