標準クエリ演算子

Last Updated 2011/09/21


標準クエリ演算子とは何かについては、最初のページの「用語の解説」で説明したとおりですが、その実体は Enumerable<T> クラスまたは Queryable<T> クラスの各メソッドです。このページではこれらのメソッドの使い方について解説します。


標準クエリ演算子の実行方法による分類

標準クエリ演算子の実行方法を大きく分けると、即時実行と遅延実行とに分類できます。さらに、遅延実行はストリーミング型と非ストリーミング型とに分けることができます。

即時実行

クエリを宣言するコード位置で、データソースを読み取り、すぐに演算を実行します。コレクションでない単一の結果を返す標準クエリ演算子は即時実行です。

遅延実行

クエリを宣言するコード位置ですぐに演算を実行しないで、foreach ステートメントなどの方法により、クエリ変数を列挙するときに初めて実行されます。つまり、クエリの実行結果は、クエリの定義時ではなくクエリの実行時のデータソースの内容に依存します。したがって、複数回クエリ変数を列挙する場合は、そのたびに結果が異なる可能性があります。

戻り値の型が IEnumerable<T> または IOrderedEnumerable<TElement> オブジェクトの場合は、ほとんどが遅延実行されます。

ストリーミング型の遅延実行

ストリーミング型の演算子の場合、要素を生成する前にソースデータを読み取る必要はありません。つまり、実行時にストリーミング(読み取り)しながらソース要素ごとに演算を実行し、必要に応じて要素を生成します。

結果の要素を生成できるまでソース要素の読み取りを続行しますので、結果の要素を 1 つ生成するために複数のソース要素を読み取る場合があります。

非ストリーミング型の遅延実行

非ストリーミング型の演算子の場合、結果の要素を生成する前にすべてのソースデータを読み取らなければなりません。ソートやグループ化などの演算はこのカテゴリに入ります。


標準クエリ演算子の使用例

まず、System.Linq.Enumerable<T> クラスの各メソッドの使い方から説明します。以下のコードは、文字列配列に対して Where および OrderBy メソッドを適用する例です。ここではあえて、デリゲートを使いました。

private void button1_Click(object sender, EventArgs e)
{
  string[] data = { "北海道", "青森", "新潟", "愛知", "和歌山", "石川", "兵庫", "広島", "鹿児島" };

  // Where メソッドを使って 3 文字のデータだけを選択する
  IEnumerable<string> resultOfWhere = Enumerable.Where<string>(data, this.Func_Where);

  // OrderBy メソッドを適用する
  IEnumerable<string> resultOfOrderBy = Enumerable.OrderBy<string, string>(resultOfWhere, this.Func_OrderBy);

  foreach (string s in resultOfOrderBy)
  {
    textBox1.Text += s + "\r\n";
  }

}

//------------------------------------------------------------------------
// 3 文字のデータだけを選択する Func デリゲート
private bool Func_Where(string s)
{
  bool result = false;

  if (s.Length == 3)
    result = true;
  
  return result;
}

//------------------------------------------------------------------------
// 昇順でソートする Func デリゲート
private string Func_OrderBy(string s)
{
  return s;
}

実行結果は以下のとおり。

鹿児島
北海道
和歌山

次に、IEnumerable<T> インターフェースを継承するクラスの拡張メソッド(標準クエリ演算子)として適用する例です。これを「メソッドベースのクエリ」と呼びます。当然ですが、実行結果は前の例と同じです。なお、メソッドの引数にはラムダ式を使用しました。

private void button2_Click(object sender, EventArgs e)
{
  string[] data = { "北海道", "青森", "新潟", "愛知", "和歌山", "石川", "兵庫", "広島", "鹿児島" };

  // 3 文字のデータだけを選択し、昇順でソートするメソッドベースのクエリ
  // ここでは where、orderby、select メソッドを使い、引数にはラムダ式を使う
  IEnumerable<string> query = data
                              .Where(s => s.Length == 3)
                              .OrderBy(s => s)
                              .Select(s => s);

  foreach (string s in query)
  {
    textBox1.Text += s + "\r\n";
  }
}

最後は標準クエリ演算子をクエリ式として適用する例です。

private void button3_Click(object sender, EventArgs e)
{
  string[] data = { "北海道", "青森", "新潟", "愛知", "和歌山", "石川", "兵庫", "広島", "鹿児島" };

  // 3 文字のデータだけを選択し、昇順でソートするクエリ
  // ここでは where、orderby、select クエリ演算子を使う
  IEnumerable<string> query = from s in data
                              where s.Length == 3
                              orderby s
                              select s;

  foreach (string s in query)
  {
    textBox1.Text += s + "\r\n";
  }
}

クエリ式は見てのとおり、プログラミングコードに代わる、宣言型のスクリプトで、これをコンパイルすると、メソッドベースのクエリと同じ実行コードを発生します。クエリ式は書くべきコード量が少なく、しかも直感的です。そうすることを最初から意図して作られたものだからです。

さて、ここでクエリ式が内部的にはどういう処理をしているかについて説明します。これから説明するコードを利用する機会はないと思いますが、クエリ式を理解する上で参考になると思います。

button1_Click イベントハンドラのコードは、次のように書くことができます。Func デリゲートをラムダ式を使って定義します。

private void button4_Click(object sender, EventArgs e)
{
  string[] data = { "北海道", "青森", "新潟", "愛知", "和歌山", "石川", "兵庫", "広島", "鹿児島" };

  Func<string, bool> func_Where = s => s.Length == 3;
  Func<string, string> func_OrderBy = s => s;

  IEnumerable<string> query = data.Where(func_Where).OrderBy(func_OrderBy);

  foreach (string s in query)
  {
    textBox1.Text += s + "\r\n";
  }
}

ラムダ式は匿名メソッドを定義するものですから、delegate 型として明示的に定義すると次のようになります。

private void button5_Click(object sender, EventArgs e)
{
  string[] data = { "北海道", "青森", "新潟", "愛知", "和歌山", "石川", "兵庫", "広島", "鹿児島" };

  Func<string, bool> func_Where = delegate(string s) { return s.Length == 3; };
  Func<string, string> func_OrderBy = delegate(string s) { return s; };

  IEnumerable<string> query = data.Where(func_Where).OrderBy(func_OrderBy);

  foreach (string s in query)
  {
    textBox1.Text += s + "\r\n";
  }
}

つまり、クエリ式はコンパイル時に button5_Click イベントハンドラと同じ内容のコードと解釈してコンパイルされます。

クエリ演算子は、名前付きメソッド、匿名メソッド、ラムダ式と組み合わせることができます。ラムダ式はより複雑な処理を比較的簡単な構文で書くことができます。


演算子の分類

各標準クエリ演算子を実行方法に基づいて分類します。なお、2 つの列にマークが付く演算子は、2 つのコレクションが関係し、各コレクションの評価は異なります。この場合、遅延実行のストリーミングで評価するのは、常に引数リストの最初のコレクションです。

標準クエリ演算子戻り値の型即時実行遅延実行 (ストリーミング)遅延実行 (非ストリーミング)
AggregateTSource--
All<TSource>Boolean--
AnyBoolean--
AsEnumerable<TSource>IEnumerable<T>--
Average1 つの数値--
Cast<TResult>IEnumerable<T>--
Concat<TSource>IEnumerable<(T>--
ContainsBoolean--
CountInt32--
DefaultIfEmptyIEnumerable<T>--
DistinctIEnumerable<T>--
ElementAt<TSource>TSource--
ElementAtOrDefault<TSource>TSource--
Empty<TResult>IEnumerable<T>--
ExceptIEnumerable<T>-
FirstTSource--
FirstOrDefaultTSource--
GroupByIEnumerable<T>--
GroupJoinIEnumerable<T>-
IntersectIEnumerable<T>-
JoinIEnumerable<T>-
LastTSource--
LastOrDefaultTSource--
LongCountInt64--
Max1 つの数値、TSource、または TResult--
Min1 つの数値、TSource、または TResult--
OfType<TResult>IEnumerable<T>--
OrderByIOrderedEnumerable<TElement>--
OrderByDescendingIOrderedEnumerable<TElement>--
RangeIEnumerable<T>--
Repeat<TResult>IEnumerable<T>--
Reverse<TSource>IEnumerable<T>--
SelectIEnumerable<T>--
SelectManyIEnumerable<T>--
SequenceEqualBoolean--
SingleTSource--
SingleOrDefaultTSource--
Skip<TSource>IEnumerable<T>--
SkipWhileIEnumerable<T>--
Sum1 つの数値--
Take<TSource>IEnumerable<T>--
TakeWhileIEnumerable<T>--
ThenByIOrderedEnumerable<TElement>--
ThenByDescendingIOrderedEnumerable<TElement>--
ToArray<TSource>TSource 配列--
ToDictionaryDictionary<TKey, TValue>--
ToList<TSource>IList<T>--
ToLookupILookup<TKey, TElement>--
UnionIEnumerable<T>--
WhereIEnumerable<T>--

NETClass

個々の標準クエリ演算子の詳細については、XML および LINQ に関係するクラスライブラリリファレンスである NETClass を参照してください。

−以上−