はじめに
.NET入門コラムの連載記事にようこそ。今回は、Language Integrated Query、略してLINQを中心に紹介します。LINQの概要とLINQに関する誤解について取り上げ、そのベースとなる言語機能を簡単におさらいしてから、LINQを使用した例をいくつか説明します。今後の連載ではLINQを何度か取り上げる予定ですが、今回がその第1弾です。
Language INtegrated Queryの概要
これまでの.NET入門コラムの記事の多くは、私が最初に書いたいくつかの記事(「Database Independent Data Access」など)までさかのぼって考えてみても、必ずと言っていいほどデータへのアクセスと処理を話題にしていました。一般に、データはデータベースに格納されますが、データにアクセスして処理する方法には、データファイル、イベントログ、Windowsのレジストリなど、他にもさまざまな形態があります。データのクエリと処理は、多くのアプリケーションに共通する要素です。
LINQ(発音は「リンク」)とは、データへのアクセスに進化をもたらす第一歩となるものです。これは、必要とされる統一性をデータアクセスに与えるプログラミングモデルであり、その対象はファイル、XML、データベース、レジストリ、イベントログから、アクティブディレクトリなどの多種多様なデータソース、さらにはFlickrなどのサードパーティ製サービスにまでおよびます。あらゆる形態のさまざまなサイズのデータを取り扱い、そのすべてに対してクエリ、設定、変換の各操作を実行できるように設計されています。IEnumerable
型を実装することそのものが、LINQの目的でもあります。
LINQに関する誤解
何かを理解しようとするときに役立つ1つのパターンは、「その対象が何であるか」の逆、つまり「その対象が何でないか」について考えることです。誰かに初めてLINQを紹介しようとするときに最もよく使われるのが、「LINQとは単なる組み込みSQLである」という表現ですが、これは正しくありません。LINQの構文は、その多くがSQLによく似た構造をしていますが、単なる組み込みSQLではありませんし、データベースへのクエリに限られたものでもありません。LINQはすべての.NET言語で無条件にサポートされるものではありません。共通言語ランタイム(CLR)に対しては、何の変更も行われていません。変更が加えられたのは、各言語とそれぞれのコンパイラのみです。このとき、言語固有の拡張が多少必要となり、Visual Basic .NET 9.0とC# 3.0には、LINQ用の統合言語サポートが組み込まれました。
LINQを実現する言語機能
LINQでは、ジェネリックを多用します。また、Visual BasicとC#の各言語には、LINQをサポートする専用の機能が多数追加されました。私の最近の記事でも、LINQに先行してこうした言語機能の一部を何度か紹介しています。次のリストは、LINQの実現に寄与する言語機能の一部と、各機能の簡単な説明です。
- 型推論 ― コンパイル時に、(式の)右辺に割り当てられた型が変数型となる簡略表現です。
- 拡張メソッド ― 新しい型を派生することなく、既存の値型または参照型を拡張できます。
- オブジェクト初期化子 ― オブジェクト初期化構文の短縮形で、等価なコードを生成できます。
- 匿名型 ― メソッドや型を生成せずに文を作成することができます。
- ラムダ式 ― インラインメソッドを簡潔に記述する方法です。
- クエリ式 ― オブジェクトを処理するコードの中に書かれるSQLライクな文です。
これらの言語機能は、機能自体に何らかのメリットがあることは確かです。しかし私見としては、これらの機能の多くはLINQ以外で使用する理由が見当たりません。
LINQの種類
さまざまなデータソースにアクセスして処理を行うため、LINQにはいくつかの種類があります。次のリストでは、Microsoftから提供されているデータドメインの一部を挙げました。今後、.NET入門ではこの中のいくつかを取り上げる予定です。
- LINQ to Objects ― オブジェクトのコレクションを操作
- LINQ to DataSets ― LINQを使用してDataSetを操作
- LINQ to SQL ― カスタム型オブジェクトと物理データベーステーブルのスキーマをマッピング
- LINQ to Entities ― 概念的なエンティティデータモデルを使用して物理データベースの概念モデルを作成
- LINQ to XML ― XMLのクエリと処理を実現
LINQ構文の概要
コードの構造と書式に大きなこだわりのある人の場合は、LINQの構文に馴染んで自分のコードに取り入れるまでに少し時間がかかるかもしれません。同様に、SQLクエリをコードに埋め込まないようにとお決まりの呪文を何度も聞かされている人も、LINQを使用することが実は悪いことでもやましいことでもなく、むしろその正反対であると納得するまでには時間を要するでしょう。
LINQがデータベースへのアクセスに限定されたものでないことは確かです。ただ、LINQを理解してもらうには、最初にSQL文を検討した後、同じ内容をコードに埋め込まれたLINQへと書き直して説明するのが一番分かりやすいでしょう。次のSQL文は、Microsoft SQL Serverに共通なサンプルデータベースNorthwindに対して作成したものです。このクエリは、所在が「Berlin」ではない顧客のリストを取得するだけの基本的なクエリです。
SELECT c.CompanyName, c.ContactName, c.City FROM Customers c WHERE c.City != 'color:red'>'Berlin' ORDER BY c.ContactName
それでは、このSQL文をLINQで書き直して、細部まで理解するために詳しく検討しましょう。クエリの構文には、クエリ式とメソッドクエリの2種類があります。ここでは、クエリ式に着目します。次のクエリ式では、GetCustomers()
から返されるIEnumerable
型を検索して、「City」の場所が「Berlin」ではないデータを取り出しています。この例の場合、GetCustomers
メソッドがデータベースにアクセスして、IEnumerable
型を返すと考えられます。
var customerNotInBerlin = from c in GetCustomers() where c.City != "Berlin" orderby c.ContactName select c;
次の表は、LINQの構文に用意されているオプションの一部をまとめたものです。
格納先 | var <変数> = | 取得した値の割り当てに型推論を使用 |
ソース | from <項目> in <データソース> | 項目セットを取得する情報ソース |
フィルタ | where <式>、distinct | 選択条件を指定する式 |
順序 | order by <式>, <式> [Ascending | Descending] | 結果の順序付け制御 |
集計 | count([<式>])、sum(<式>)、min(<式>)、max(<式>)、avg(<式>) | ソース項目の集計 |
射影 | select <式> | 出力の生成 |
上記の項目以外にも、たくさんのオプションとバリエーションが構文に存在しますが、理解するための出発点としてはこれで十分です。