Last Updated 2011/09/21
LINQ クエリとは、LINQ によるクエリ操作と考えてください。LINQ クエリの基本的な手順は次の 3 段階に分かれます。
データソースとは、LINQ クエリの操作対象となるもので、.NET Framework における LINQ クエリの操作対象は IEnumerable または IEnumerable<T> インターフェースを継承するオブジェクトです。
LINQ クエリは、標準クエリ演算子を使ってクエリ式を作成します。
LINQ クエリの結果は、標準クエリ演算子で説明するとおり、通常は遅延実行されます。
標準クエリ演算子の機能は、次の 5 つに分類できます。
フィルター処理 | 指定の条件を満足する要素だけを抜き出す操作です。 |
ソート | 指定のキーに基づいて要素を昇順または降順で並び替えます。 |
グループ化 | 指定のキーに基づいて要素をグループ化します。 |
結合 | 異なるコレクション間で要素どうしを関連付けることです。 |
選択(投影) | 実行結果の型を指定します。 |
C# コードによるクエリ式を構築するときに便利なようにキーワードが設定されています。これらはコードをコンパイルするときに、Enumerable<T> クラスまたはその派生クラスで実装されたメソッドに変換されます。
処理内容 | キーワード | 関連するキーワード |
---|---|---|
フィルター処理 | where | |
ソート | orderby | ascending descending |
グループ化 | group | by into |
結合 | join | equals in into on |
選択(投影) | select | |
データソースの指定 | from | in |
変数への代入 | let |
これらのキーワードの使い方は、NETClassを参照してください。
LINQ クエリは、前の項で説明したクエリキーワードを使う方法がもっとも便利ですが、その実装メソッドを直接呼び出すことも可能です。こういう手順を総称して、「メソッド構文」と呼びます。また、クエリキーワードとメソッド構文とを混在させることも可能です。
次のコードはクエリ式による方法です。
private void button1_Click(object sender, EventArgs e) { string[] data = { "北海道", "青森", "新潟", "愛知", "和歌山", "石川", "兵庫", "広島", "鹿児島" }; // 3 文字のデータだけを選択し、昇順でソートするクエリ // ここでは where、orderby、select クエリ演算子を使う var query = from s in data where s.Length == 3 orderby s select s; foreach (string s in query) { textBox1.Text += s + "\r\n"; } }
実行結果は以下のとおり。
鹿児島 北海道 和歌山
次はメソッド構文による方法です。実行結果は当然ですが、前の例と同じです。
private void button2_Click(object sender, EventArgs e) { string[] data = { "北海道", "青森", "新潟", "愛知", "和歌山", "石川", "兵庫", "広島", "鹿児島" }; // 3 文字のデータだけを選択し、昇順でソートするメソッドベースのクエリ // ここでは where、orderby、select メソッドを使い、引数にはラムダ式を使う var query = data .Where(s => s.Length == 3) .OrderBy(s => s) .Select(s => s); foreach (string s in query) { textBox1.Text += s + "\r\n"; } }
クエリキーワードだけで構成するほうが直感的であることは一目瞭然ですね。
いずれにしろ、クエリキーワードだけですべての処理が間に合うわけではありません。適宜使い分けることが肝要です。どんなメソッドがあるかについては、標準クエリ演算子のページをご覧ください。
PLINQ のページで紹介する使用例を見てもらえば分かりますが、操作対象のテキストファイルの文字コードを判定するとき、判定に失敗すると null を返す場合があります。文字コードが不明であればそれ以上の処理を続けることはできませんからそのファイルは操作対象から除外しなければなりません。
以下は部分的なコードですが、null 値を処理する例です。where キーワードを使って null のデータを除外しています。
var query1 = from c in categories where c != null join p in products on c.ID equals (p == null ? null : p.CategoryID) select new { Category = c.Name, Name = p.Name };
クエリ式内で任意のメソッドを呼び出すことができますが、メソッドを呼び出したときに例外が発生した場合の処理方法について説明します。
private void button1_Click(object sender, RoutedEventArgs e) { IEnumerable<int> dataSource; try { dataSource = this.GetData(); } catch (InvalidOperationException) { MessageBox.Show("Invalid operation"); goto Exit; } // 例外が発生しなかったとき(もちろん、今回はありえないが) var query = from i in dataSource select i * i; foreach (var i in query) { textBox.Text += String.Format("{0}\r\n", i); } // 例外発生時、ここにジャンプしてくる Exit: MessageBox.Show("Exit"); } // クエリ式を作成するメソッド // ただし、今回は例外を発生させるだけ private IEnumerable<int> GetData() { throw new InvalidOperationException(); }
場合によっては、クエリ式の実行を即座に停止することがクエリ内で発生する例外に対する最良の対処法になることがあります。
private void button1_Click(object sender, RoutedEventArgs e) { string[] files = { "fileA.txt", "fileB.txt", "fileC.txt" }; var exceptionDemoQuery = from file in files let n = SomeMethodThatMightThrow(file) select n; // クエリを実行したときに例外を発生するので、ここで例外を捕捉する try { foreach (var item in exceptionDemoQuery) { textBox.Text += String.Format("Processing {0}\r\n", item); } } catch (InvalidOperationException ex) { textBox.Text += ex.Message; } finally { // 必要なら } } private string SomeMethodThatMightThrow(string s) { // 5 文字目が "C" のとき、例外を発生させる if (s[4] == 'C') throw new InvalidOperationException(); return @"C:\newFolder\" + s; }
実行結果:
Processing C:\newFolder\fileA.txt Processing C:\newFolder\fileB.txt オブジェクトの現在の状態に問題があるため、操作は有効ではありません。
−以上−