連載:[完全版]究極のC#プログラミング

Chapter15 LINQとクエリ式

川俣 晶
2010/03/17
Page 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

15.6 LINQの本質は列挙

 さて、この例を見て何の意味もないプログラムだと思った読者もいるだろう。だがそうではない。このプログラムは、次のプログラムと比較することで、LINQの重要な性質を明らかにできるのである。ではその話題に進もう。

 先ほど「LINQを使って記述できる処理は、LINQを使用しなくても書くことができる。foreach文で繰り返しを行えばよい」と書いた。そこで、前節の例を、LINQではなくforeach文で書き直してみよう(リスト15.3参照)。

using System;
using System.Collections.Generic;

class Program
{
  static void Main(string[] args)
  {
    int[] array = { 1, 2, 3 };
    var list = new List<int>();

    foreach (int n in array) list.Add(n); // クエリ式を置き換えた行
    foreach (int n in list) Console.WriteLine(n);
    // 出力:
    // 1
    // 2
    // 3
  }
}
リスト15.3 リスト15.2のクエリ式をforeach文に置き換えた

 この2つのプログラムの動作は、使用されるコレクションの種類などの瑣末な問題を除けば、同じといってよいのだろうか? つまり、

var query = from x in array select x;

を、

var list = new List<int>();
foreach (int n in array) list.Add(n);

に置き換えて、つねに同じ結果が得られると期待してよいのだろうか?

 実はそうではない。たった1行追加するだけで、その差を見ることができる(リスト15.4、次ページリスト15.5参照)。

static void Main(string[] args)
{
  int[] array = { 1, 2, 3 };

  var query = from x in array select x;

  array[1] = 4;  // 追加行

  foreach (int n in query) Console.WriteLine(n);
  // 出力:
  // 1
  // 4
  // 3
}
リスト15.4 クエリ式版(リスト15.2)に1行追加(抜粋)

static void Main(string[] args)
{
  int[] array = { 1, 2, 3 };
  var list = new List<int>();
  foreach (int n in array) list.Add(n);

  array[1] = 4;  // 追加行

  foreach (int n in list) Console.WriteLine(n);
  // 出力:
  // 1
  // 2
  // 3
}
リスト15.5 foreach版(リスト15.3)に1行追加(抜粋)

 見てのとおり、1行書き加えただけで、結果は違ってしまった。値を書き換えた結果は、クエリ式版にのみ反映されている。なぜかといえば、foreach版がコレクションの複製を作成しているのに対して、クエリ式版は列挙を行うオブジェクトを作成しているという相違があるためだ。つまり、クエリ式版は列挙が行われるまで結果が何になるか確定しない。クエリ対象が変化すれば、列挙結果も変化する。

 したがって、foreach版の動作をクエリ式版に近づけるには、コレクションを複写するのではなく、列挙インターフェースを取得して、それを変数に保存するとよい(リスト15.6参照)。実行されている機能はまったく異なっているが、少なくとも結果は同じになる。

using System;
using System.Collections.Generic;

class Program
{
  static void Main(string[] args)
  {
    int[] array = { 1, 2, 3 };

    // 列挙インターフェースの取得
    IEnumerable<int> enumerator = array;

    array[1] = 4;  // 追加行

    foreach (int n in enumerator) Console.WriteLine(n);
    // 出力:
    // 1
    // 4
    // 3
  }
}
リスト15.6 リスト15.2は本当はこれに近い


 INDEX
  [完全版]究極のC#プログラミング
  Chapter15 LINQとクエリ式
    1.15.1 LINQの面白さ
    2.15.2 LINQとは何か?
    3.15.3 「値の集まり」に対する演算
    4.15.4 なぜLINQなのか?
    5.15.5 最も基本的なLINQ
  6.15.6 LINQの本質は列挙
    7.15.7 LINQを使ううえでの注意点
    8.15.8 クエリ結果を加工する
    9.15.9 複数のソースからクエリする
    10.15.10 条件で絞り込む
    11.15.11 一部項目のみselectする
    12.15.12 シンプルなソート
    13.15.13 クエリの接続
    14.15.14 クエリ結果のグループ化
    15.15.15 複数ソースを関連付けるjoin句
    16.15.16 from句とjoin句のパフォーマンス
    17.15.17 join句のグループ化結合
    18.15.18 join句の左外部結合
    19.15.19 単独で使うDefaultIfEmptyメソッド
    20.15.20 内部列挙を伴うfrom句の二重使用
    21.15.21 let句
    22.15.22 クエリのインスタンス化
    23.15.23 クエリ結果の個数を得る
    24.15.24 Anyメソッドと存在チェック/【C#olumn】クエリ式のデバッグテクニック
    25.15.25 まとめ/【C#olumn】LINQの難しさ/【Exercise】練習問題
 
インデックス・ページヘ  「[完全版]究極のC#プログラミング」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間