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

Chapter16 LINQとメソッド構文

川俣 晶
2010/03/29
Page 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

16.14 メソッド構文の左外部結合

 join句を用いた3種類の結合の最後は左外部結合である。しかし、もともと左外部結合はクエリ式とはいっても、DefaultIfEmptyというメソッドの力を借りて実現していたものなので、メソッド構文とは相性が良いはずである。……と思って軽視していたのだが、いざ書き直してみると一筋縄ではいかい。

 たとえば、次は前章リスト15.23で紹介した左外部結合のクエリ式である。

var query = from x in 商品情報データ
            join y in 商品販売価格データ on x.Id equals y.Id into z
            from a in z.DefaultIfEmpty(
                        new 商品販売価格() { 店名 = "取扱店なし" })
            select new { Name = x.名前, 販売店 = a };

 これをメソッド構文に書き直したところ、次のような内容になった。

var query
    = 商品情報データ
        .GroupJoin(
            商品販売価格データ,
            (x) => x.Id,
            (y) => y.Id,
            (x, z) => new {
                Name = x.名前,
                販売店 = z.DefaultIfEmpty(
                         new 商品販売価格() { 店名 = "取扱店なし" })
            })
          .SelectMany(
            (x) => x.販売店,
            (x, a) => new { Name = x.Name, 販売店 = a } );

 このとおり、クエリ式では1つしかなかったnewが2つに増えてしまっている。しかし、これは効率が落ちたことを意味するわけではない。クエリ式を使ったコードをリリースビルドして.NET ReflectorのC#モードで逆コンパイルすると、上記のメソッド構文とほぼ同等のコードが生成されていることがわかる。つまり、ソースコードの見た目上はnewの数が増えているが、実行ファイルに含まれるnew呼び出しの数は変わらないわけである。

 余談だが、実は上記のクエリ式のメソッド構文への書き換えは、なかなかうまく書けずに非常に焦った。検索しても書き換え事例はヒットしてこないし、頑張って書いたクエリも上記のように冗長感が漂う。最終的にハタと気づいたのは、クエリ式をコンパイルして.NET Reflectorで逆コンパイルするという方法であった。それにより、自分で書ける最善のコードとリリースビルドの逆コンパイル結果がほぼ一致したことで、やっと「間違っていなかった」と納得することができた。

 なお、逆コンパイルは有効であることを特に付記しておこう。クエリ式をメソッド構文に書き換えたいケースは、クエリ式では記述できない機能を使うなど、いくつかの事例が考えられる。しかし、込み入ったクエリ式は、とっさにどのようなメソッド構文に置き換えれば等価であるかわかりにくいこともある。そういう場合は、コンパイルして.NET Reflectorで逆コンパイルするという方法が使用できる。クエリ式は糖衣構文であるため、逆コンパイルでは(いまのところ)復元されない。その結果、逆コンパイル出力は、実体として生成されたメソッド構文になるわけである。

 さて、話を戻そう。このコードの内容を説明しておく。このコードは、GroupJoinメソッド自体がSelect機能を含むため、それとSelectManyメソッドにより、Select相当の機能が2回動作している。1回目は、次の部分がそれに当たる。

new {
  Name = x.名前,
  販売店 = z.DefaultIfEmpty(
           new 商品販売価格() { 店名 = "取扱店なし" })

 ここでは「商品の名前」と「販売店の列挙」のペアとなる結果を生成しているが、その際に、DefaultIfEmptyメソッドを用いて、対応する販売店がない商品名について「取扱店なし」という「デフォルト値を持つ列挙」を挿入している。

 2回目は、次の部分がそれに当たる。

SelectMany(
    (x) => x.販売店,
    (x, a) => new { Name = x.Name, 販売店 = a }

 これにより、1回目では列挙であった「販売店」を展開して、商品の名前と販売店のペアを生成している。

 まとめると、第1段階では「値」と「列挙」のペア(すべて列挙するには二重の繰り返しを要する)を生成したが、第2段階では「値」と「値」のペア(単層の繰り返しですべて列挙できる)を生成していることになる。


 INDEX
  [完全版]究極のC#プログラミング
  Chapter16 LINQとメソッド構文
    1.16.1 予約語のエスケープ
    2.16.2 メソッド構文のLINQ
    3.16.3 クエリ式とメソッド構文の違い
    4.16.4 絞り込みと結果の生成
    5.16.5 最初の事例の別解
    6.16.6 メソッド構文でのみ可能なクエリ
    7.16.7 メソッド構文のソート
    8.16.8 orderbyの比較オブジェクト
    9.16.9 メソッド構文の複数のソースからクエリする
    10.16.10 メソッド構文のクエリの接続
    11.16.11 クエリ結果のグループ化
    12.16.12 メソッド構文で複数のソースを関連付ける
    13.16.13 メソッド構文のグループ化結合
  14.16.14 メソッド構文の左外部結合
    15.16.15 メソッド構文のlet句/【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 記事ランキング

本日 月間