Visual Basic LINQ講座
VB2008 対応

 

Visual Basic 中学校 > LINQ講座 >

第3回 LINQの動作

LINQの遅延実行と即時実行をAggregateの使用方法も含めて説明します。また、メソッドベースの構文についても説明します。

概要

・基本的にLINQは実際に値が必要になるまで実行されない。これを遅延実行と呼ぶ。

・LINQをすぐに実行したい場合はAggregateを使うか、ToListやToArrayなどを使用して値を要求する。これを即時実行と呼ぶ。

・遅延実行の場合は配列やコレクションに対するLINQの戻り値はIEnumerable(Of …)。

・即時実行の場合はLINQの戻り値は要求した値の型。

・Aggregateを使うと、合計や個数、平均などの集計を行うことができる。

・LINQのキーワードを通常のメソッドとして呼び出すメソッドベースの構文の説明

 

1.LINQが実行されるタイミング

 

従来のVBのメソッドやプロパティは呼び出したときに戻り値を受け取る仕組みでしたが、通常のLINQは戻り値を使おうとしたときに値を受け取ります。

このタイミングでの実行されるという仕組みは今までのVBの常識とは大きく異なりますから、LINQを使う際にはポイントとして忘れないように覚えておきましょう。

たとえば、前回も紹介した次のLINQを考えてみます。

VB2008 対応

'データソースの作成
Dim animals = New String() {"アメンボ", "イノシシ", "ウマ", "エリマキトカゲ", "オオカミ"}

'LINQで処理を定義
Dim results = From animal In animals

'結果セットを取得して表示
ListBox1.Items.AddRange(results.ToArray)

■リスト1

このLINQはなんの機能もない説明用のシンプルなものです。

このプログラムを見るとDim results = From animal In animalsの行でLINQを実行しているようにも見えますが、実は違います。LINQが実行されるのは最後の行なのです。

このことを、はっきりさせるためにプログラムに1行追加して次のようにしてみます。

VB2008 対応

'データソースの作成
Dim animals = New String() {"アメンボ", "イノシシ", "ウマ", "エリマキトカゲ", "オオカミ"}

'LINQで処理を定義
Dim results = From animal In animals

animals(0) = "水黽"
'←これは「あめんぼ」を漢字で表現したもの

'結果セットを取得して表示
ListBox1.Items.AddRange(results.ToArray)

■リスト2

ListBox1に結果を表示する直前に、データソースの値を変更するようにしました。従来のVBの常識から行くと、resultsに値をセットしてからデータソースを変更しているので結果はアメンボ、イノシシ、ウマ、エリマキトカゲ、オオカミになりそうに思えますが、実際には水黽、イノシシ、ウマ、エリマキトカゲ、オオカミになるのです。

LINQではLINQの構文を使って変数(この例ではresults)をセットした時点では、値を抽出する「方法」だけが決まり、その方法を使って実際の抽出を行うのは実際に値が必要になったタイミングなのです。

この仕組みを遅延実行と呼びます。

 

もう1つ遅延実行を印象付ける例を紹介しましょう。

VB2008 対応

Dim targetLengh = 4

'データソースの作成
Dim animals = New String() {"アメンボ", "イノシシ", "ウマ", "エリマキトカゲ", "オオカミ"}

'LINQで処理を定義
Dim results = From animal In animals Where Len(animal) >= targetLengh

targetLengh = 5

'結果セットを取得して表示
ListBox1.Items.AddRange(results.ToArray)

■リスト3

この例では文字列の長さを抽出条件にしています。最初条件を表す変数targetLengthは4なのでウマ以外の要素はすべて抽出対象になっていますが、最後に実際に値を表示する直前にtargetLengthを5に変更しています。

どうなるかわかりますよね?

結果はエリマキトカゲだけが抽出されます。

 

この性質を利用すると、次のような多段階クエリのときにパフォーマンス面で有利になります。

VB2008 対応

'データソースの作成
Dim animals = New String() {"アメンボ", "イノシシ", "ウマ", "エリマキトカゲ", "オオカミ"}

'LINQで処理を定義

'第1段階 文字数4文字以上のものを抽出
Dim results = From animal In animals Where animal.Length >= 4

'第2段階 先頭の1文字のみを抽出
results = From animal In results Select Strings.Left(animal, 1)

'結果セットを取得して表示
ListBox1.Items.AddRange(results.ToArray)

■リスト4

この例ではクエリの結果をさらにクエリしています。LINQを2回定義しているのですが実際にクエリが実行されるのはListBoxに結果を表示するときの1回だけです。クエリ式を定義するたびにLINQが実行されるわけではないので、実行するまではクエリ式を組み合わせていってもパフォーマンスの低下はほとんどありません。

もっとも、この例ではどちらも簡単なクエリですからはじめから1回で定義した方がいいでしょう。このような多段階クエリが特に効力を発揮するのはデータベースに対してクエリを行う時です。データベースからデータを取得して、その取得した結果に対してまた集計やら何やらの処理を行うということは珍しくありません。そんな処理をLINQで書いておけば、実際に実行されるのは値が必要になった最後の1回だけですからパフォーマンスの劣化が気にならないのです。

今までパフォーマンスの劣化が気になってがんばって長大で難解なSQL文を書いた記憶はありませんか?LINQの遅延実行がその苦労を解消してくれるかもしれません。

発展 発展学習  -  多段階実行の仕組み

発展学習では意欲的な方のために現段階では特に理解する必要はない項目を解説します。

このような多段階実行ができるのはLINQプロバイダ、つまりLINQの実行エンジンがクエリ式を「命令のツリー」と解釈することができるからです。クエリに対してさらにクエリを定義した場合は、この「命令のツリー」の枝が増えるイメージです。

LINQプロバイダは常にクエリ式を命令のツリーとしてコンパイルするわけではなく、通常のコードと同じように コンパイルすることもあります。

当面はLINQ内部の仕組みには立ち入らないことにしますが、VB2008の新機能に深く関連する面白いテーマなので少しだけこのことを示唆するサンプルを紹介しておきます。LINQの使用には直接関係がないのでわからなければ現時点で無理に理解する必要はありません。

次の例では実際に変数commandを使ってラムダ式で定義した関数を呼び出すことができます。これは、ラムダ式が実際に実行可能なコードとしてコンパイルされていることを示します。

VB2008 対応

Dim command As Func(Of String, Boolean) = Function(Name) Name.Length >= 4

MsgBox(command("カラス")) 'Falseと表示

■リスト5

一方次の例では変数commandは実行できません。変数commandにはラムダ式で表現される実行可能なコードではなく、ラムダ式を命令のツリーにしたものがセットされるのです。

VB2008 対応

Dim command As Expressions.Expression(Of Func(Of String, Boolean)) = Function(Name) Name.Length >= 4

'MsgBox(command("カラス")) 'ビルドエラー

■リスト6

つまり、コンパイラはまったく同じラムダ式を 、ある時は実際に実行可能なコードとして解釈することもできるし、また別のある時には単なる命令のツリーとして解釈することもできるということを示しています。

LINQの中で指定している式は他ならぬラムダ式です。コンパイラは都合のいいようにラムダ式を解釈しているというわけです。

2つ目の例では次のようにすると命令のツリーの中身を見ることができます。ちゃんと書くと大変なのでここでは使用している演算子だけを調べてみます。

VB2008 対応

Dim command As Expressions.Expression(Of Func(Of String, Boolean)) = Function(Name) Name.Length >= 4
Dim operation = command.Body.NodeType

MsgBox(operation.ToString)
'GreaterThanOrEqualと表示

■リスト7

この場合は、GreaterThanOrEqualと表示されます。つまり、ラムダ式の中で >= による評価が行われていることを示しています。ためしにName.Length >= 4 の部分の = を取って Name.Length > 4 とすると、今後はGreaterThanと表示されます。ちゃんとラムダ式の内容を読み取って構造化しているのです。

 

2.即時実行

 

何かの事情で遅延実行したくない場合などにクエリを即時に実行することもできます。

即時実行を行うには厳密にはAggregate演算子を使用する以外の方法はないのですが、遅延実行の場合でも値が必要になればクエリが実行されるという性質があるので、クエリの定義と同時にToArrayToListなどのメソッドを使って具体的な値を要求すれば事実上即時実行を行うことができます。

ですので、一般的には即時実行を行うにはAggregateToArrayToListの3つが選択的に使用されます。もちろん、値の要求を行うそのほかのメソッドやFor Eachなどの構文でも即時実行は可能です。

要するにクエリの結果を後で使う場合は自動的に遅延実行になり、クエリの値をすぐ使う場合は即時実行となるというわけです。

Aggregateはこの講座では新しく登場するLINQの演算子なので後で説明することにして、ここではクエリの定義とともに値を要求する例を紹介します。

 

次の例はさきほど遅延実行の例として紹介したものとほとんど同じですが、LINQの定義時にToListメソッドを使用している点だけが異なります。

VB2008 対応

Dim targetLengh = 4

'データソースの作成
Dim animals = New String() {"アメンボ", "イノシシ", "ウマ", "エリマキトカゲ", "オオカミ"}

'LINQで処理を定義
Dim results = (From animal In animals Where Len(animal) >= targetLengh).ToList

targetLengh = 5

'結果セットを取得して表示
ListBox1.Items.AddRange(results.ToArray)

■リスト8

ToListメソッドはListクラスを返すメソッドです。このメソッドを使うことでVBはクエリの結果をすぐにListクラスに変換しようとします。そのため即時実行が行われます。

結果としてListBox1にはアメンボ・イノシシ・エリマキトカゲ・オオカミの4つが表示されます。もし、ToListがなければ結果はエ リマキトカゲだけです。

なお、LINQ全体をかっこで囲んでいるのは、こうしないと何にToListメソッドを適用するのか不明確になってしまうからというVBの構文上の理由で、LINQとは直接関係ありません。

 

3.LINQの戻り値

 

LINQを定義している行だけ抜き出して、遅延実行と即時実行を比較してみましょう。

まず、遅延実行の場合です。

VB2008 対応


Dim
results = From animal In animals Where Len(animal) >= targetLengh
 

■リスト9

次に、即時実行の場合です。

VB2008 対応


Dim
results = (From animal In animals Where Len(animal) >= targetLengh).ToList
 

■リスト10

素直にみると、たしかにToListメソッドがあるかないかだけの違いですが、VBにとってこの2つにはもっと大きな違いがあります。

それは変数resultsの型です。VB2008からは変数を定義するときに型を明示しなくても自動的に推定してくれるようになりました。そのため、LINQのように複雑な(または、面倒な)型を返す変数にはAsをつけないのが通例となっています。

この場合、変数にマウスカーソルを合わせるとVBがどの型を採用したかがわかります。

遅延実行の場合ResultsIEnumerable(Of String)型になります。IEnumerable(Of …)型は、配列やコレクションを対象にしたLINQ(=LINQ to Object)の通常の戻り値の型です。この型はジェネリックなので…の部分には状況に応じて異なる型になります。今回はString型の配列から抽出を行っているのでString型という判定になっています。Select句の内容によって型が変わる場合もあります。

一方、即時実行の場合resultsList(Of String)型になります。これはToListメソッドがListクラスを返すメソッドであることから容易に想像が付きます。

他にToArrayメソッドで即時実行をした場合は戻り値はString()、つまり文字列型の配列になります。

戻り値の型が違うので、この後の処理でresultsで使用できるメソッドやプロパティも当然変わります。ですから、実行されるタイミングと合わせてこのような型の違いも覚えておく必要があります。

 

戻り値がもっと複雑な形式になるときVBはどのような型を導出するか、この機会に考えてみましょう。

たとえば、Selectで2つの項目を選んだ場合戻り値は何型になるでしょうか?

VB2008 対応

'データソースの作成
Dim animals = New String() {"アメンボ", "イノシシ", "ウマ", "エリマキトカゲ", "オオカミ"}

'LINQで処理を定義
Dim results = From animal In animals Select animal, animal.Length

'結果セットの最初の項目を取得(LINQが実行される)
Dim first = results(0)

MsgBox("最初の動物は" & first.animal & "で" & first.Length & "文字です。")

■リスト11

遅延実行なのでIEnumerable(Of …)になるという予想はできるのですが、「…」の部分がどうなるかちょっと考えてみてください。

 

答えはちょっとずるいんですがIEnumerable(Of 匿名型)です。こんなところでVB2008から導入された匿名型が使われているのです。匿名型も立派な型ですから、この後で匿名型のプロパティを使ってLINQの結果にアクセスできます。この例ではanimalプロパティとLengthプロパティを実装した型になります。

匿名型のプロパティの名前を自分でつけたいときはSelect句内で「変数名 =」という構文を使用します。

一例を紹介します。

VB2008 対応

'データソースの作成
Dim animals = New String() {"アメンボ", "イノシシ", "ウマ", "エリマキトカゲ", "オオカミ"}

'LINQで処理を定義
Dim results = From animal In animals Select name = animal, size = animal.Length

'結果セットの最初の項目を取得(LINQが実行される)
Dim first = results(0)

MsgBox("最初の動物は" & first.name & "で" & first.size & "文字です。")

■リスト12

 

ヒント 匿名型

念のため匿名型について少し補足しておきます。VB2005までは匿名型がなかったのでプログラムで使用する型は事前にどこかで定義しておく必要がありました。これは何かの名前を付けたクラスや構造体です。

ところが、VB2008からは単純なものに限られますがその場で型を定義できるようになりました。これが匿名型です。たとえば、NameプロパティとSizeプロパティをもったその場限定の型を定義するには次のようにします。

VB2008 対応

Dim Mine = New With {.Name = "カラス", .Size = 3}

Mine.Name =
"キリン"

MsgBox(Mine.Name & ", " & Mine.Size) '「キリン, 3」と表示される。

■リスト13

このNew Withという書き方が匿名型が使用される合図です。

このとき変数Mineの型には名前がないので「匿名型」と呼ばれます。本当はコンパイラが適当に名前をつけてしまうのですが、この名前はコンパイルされるたびに変更される可能性があるので名前に依存した処理を書いてはいけないことになっています。

 

4.Aggregate

 

Aggregateはデータソースを順番に処理する方法で集計して、結果を単一の値として返します。集計の内容は合計、平均、最大、最小などが用意されています。集計を自作のメソッドで定義することもできますがその方法は別の機会に説明します。

Aggregateは戻り値がIntegerなど普通の型になり、集計は即時実行されます。

まず、単純に値を合計する例を紹介します。

VB2008 対応

'データソースの作成
Dim numbers() As Integer = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

'Aggregateで集計実行(即時実行)
Dim result As Integer = Aggregate number In numbers Into Sum()

'結果を表示
MsgBox(result) '55と表示される

■リスト14

今まで紹介したLINQのクエリ式はすべてFromから始まっていましたが、今回はAggregateから始まります。クエリ式は必ずFromAggregateのどちらかで開始します。

Aggregateの構文は難しくありません。

VB2008 対応

Aggregate 反復変数 In データソース Into 集計関数

集計関数は代表的なものがあらかじめ用意されているので通常はそれらを使用します。集計関数は「アキュームレート関数」と呼ばれることもあります。

用意されている集計関数は以下のとおりです。

集計関数 機能
All データソースのすべての要素が指定した条件を満たしていればTrueを返す。
Any データソースのどれかの要素が指定した条件を満たしていればTrueを返す。
Average 平均値を返す。
Count 個数を返す。カウント対象を指定する条件を記述することもできます。
First 最初の値を返す。
FirstOrDefault 最初の値を返す。データソースに要素が1つも含まれていない場合型の既定値を返す。(例:Integerなら0)
Last 最後の値を返す。
LastOrDefault 最後の値を返す。データソースに要素が1つも含まれていない場合型の既定値を返す。(例:Integerなら0)
LongCount Countと同じですが個数をLong型で数えます。(普通は使いません)。
Max 最大値を返す。
Min 最小値を返す。
Sum 合計値を返す。

■表1

上述のSumが使えればAverageMaxなどは同じ方法で簡単に使用できます。ここでは説明があった方がいいと思ったAllAnyCountをピックアップして紹介します。

まず、AllAnyはデータソースの全ての要素が指定した条件に適合するかを調べます。Allはすべて適合すればTrueを、Anyは1つでも適合するものがあればTrueを返します。

たとえば、以下のLINQでは、フォームに貼りついているすべてのTextBoxの中で何も入力されていないものがあるか調べます。

VB2008 対応

'Aggregateで集計実行(即時実行)
Dim result = Aggregate txt In Me.Controls.OfType(Of TextBox)() Into Any(txt.Text.Trim = "")

'結果を表示
If result Then
    MsgBox("空のTextBoxがあります!")
Else
   
MsgBox("すべてのTextBoxには何か入力されています。")
End
If

■リスト15

この例では、「すべてのTextBox」で構成されるデータソースを作成するためにOfTypeを使用しています。OfTypeは前回も登場しましたが、このように雑多な要素の中から特定の型の要素だけで構成されるデータソースを選抜する便利なメソッドです。

集計関数のAnyは引数として条件を指定します。条件は必ずTrueFalseで評価できるものでなければいけません。If文で指定できるものと同じと考えると良いでしょう。

Allの使い方はAnyと全く一緒で、この例のAnyの部分をAllに変えるだけです。結果はAllの場合はすべてのTextBoxが空の場合にTrueを返すという違いがあります。

 

この例は個数を数えるCountを使用して記述することもできます。次のようになります。

VB2008 対応

'Aggregateで集計実行(即時実行)
Dim result = Aggregate txt In Me.Controls.OfType(Of TextBox)() Into Count(txt.Text.Trim = "")

'結果を表示
If result > 0 Then
    MsgBox("空のTextBoxがあります!")
Else
   
MsgBox("すべてのTextBoxには何か入力されています。")
End
If

■リスト16

Countは戻り値は数値型になりますが、条件をつけられる点はAnyAllと同じです。もちろん条件をつけないで使用することもできて、その場合はすべての要素の数を返します。

さらに、Where句で条件を定義することもできます。

VB2008 対応

'Aggregateで集計実行(即時実行)
Dim result = Aggregate txt In Me.Controls.OfType(Of TextBox)() Where txt.Text.Trim = "" Into Count()

'結果を表示
If result > 0 Then
    MsgBox("空のTextBoxがあります!")
Else
   
MsgBox("すべてのTextBoxには何か入力されています。")
End
If

■リスト17

この書き方の方が今まで登場してきたLINQに近いです。お好みの書き方を使用されて構いません。Whereの位置はIntoの前になるので気を付けてください。もし間違ってもすぐに警告がでるので気がつくとは思いますが・・・。基本的にLINQのクエリ式では処理する順番にキーワードを書いていく形になっているようです。この例では条件をしぼってから数を数えたいのであって、数を数えてから条件を絞りたいのではありません。ですから、WhereIntoの形になるというイメージです。あくまでイメージであってすべてが処理の順番になっているわけではありませんが・・・。

 

ところで、Aggregateは単純に集計するだけにしては構文が大袈裟(おおげさ)なように見えるかもしれません。

もう一度Aggregateを使用している部分をピックアップしてみます。

VB2008 対応


Dim
result As Integer = Aggregate number In numbers Into Sum()
 

■リスト18

単純に集計するだけならnumbers.Sumのように書くだけでいいはずです。実際、この後で紹介するメソッドベースの構文ではそのような書き方も可能です。

では、どうして長々と書く必要があるのでしょうか?

これはAggregateの実行方法が関係します。AggregateSumCountだけ見ていると単純に集計を実行しているだけのように見えますが、内部ではデータソースの要素を1つずつ処理しているのです。ループのようなイメージです。そして、集計関数を自作する場合はこの内部処理に合わせた形で記述していく場合があります。Aggregateの構文はこの内部の処理方式を反映したものになっているというわけです。

それにWhereFromと混ぜて使う場合にも便利です。FromAggregateを同時に使う例はもっと後の回で紹介する予定です。

最後に、英語のAggregateの意味が「まとめる」や「総計する」という意味である点もしておきしておきます。だから、Aggregate number In numbers Into Sumとは、「numbersの中のnumberを合計にまとめなさい」というニュアンスのある文章にもなっているのです。

 

5.メソッドベースの構文

 

さて、前回ちょっとだけ説明しましたが、LINQでクエリを記述するにはクエリ式の構文とメソッドベースの構文という2つの書き方があります。

今まで紹介してきたFromAggregateから始まる構文はすべてクエリ式の構文です。ここでは、LINQの詳しい話を進める前に立ち止まってメソッドベースの構文を紹介しておきます。

まず、クエリ式の構文もメソッドベースの構文も書き方が違うだけでだいたい同じようなことができるという点を押さえておいてください。なので、とりあえずクエリ式の構文が使えれば普通は問題ありません。

ためしに同じクエリを2つの構文で書いてみましょう。

まずはクエリ式の構文です。

この例では動物の名前から4文字以上の動物を抽出します。いつも同じような例で退屈されるかもしれませんね。

VB2008 対応

'データソースの作成
Dim animals = New String() {"アメンボ", "イノシシ", "ウマ", "エリマキトカゲ", "オオカミ"}

'LINQで処理を定義
Dim results = From animal In animals Where Len(animal) >= 4

'結果セットを取得して表示
ListBox1.Items.AddRange(results.ToArray)

■リスト19

これとまったく同じことをメソッドベースの構文で書くことこうなります。

VB2008 対応

'データソースの作成
Dim animals = New String() {"アメンボ", "イノシシ", "ウマ", "エリマキトカゲ", "オオカミ"}

'LINQで処理を定義
Dim results = animals.Where(Function(animal) Len(animal) >= 4)

'結果セットを取得して表示
ListBox1.Items.AddRange(results.ToArray)

■リスト20

VB2008から導入された新機能のことを知らないでこの例を見ると目が点になるかと思います。「なぜに引数の中にFunctionが!?」と。このFunctionはラムダ式なのです。

そこで、ちょっと模式化して書くと次のようにしてクエリを定義していることになります。


animals.Where(ラムダ式)
 

■リスト21

これで構造がだいぶすっきりすると思います。要するにWhereとは実はanimalsのメソッドだったのです。それをVBは特別にクエリ式の構文のような書き方で呼び出せるようにしただけなのです。本質はあくまでメソッドです。

ではWhereは何クラスのメソッドかというと、IEnumerableインターフェースのメソッドです。AggregateCountOfTypeなど今まででてきたLINQのキーワードの多くはIEnumerableインターフェースのメソッドだったのです。インターフェースがメソッドを持っているというと妙に思われるかもしれませんが、これもVB2008の新機能である拡張メソッドを使用すると可能です。

ラムダ式の部分に目を移しましょう。ラムダ式とは簡単に言うと即席関数です。ちょっとだけ関数が必要になった場合にわざわざ別途定義しなくても必要最小限の情報だけ書いて使えるようにしたのがラムダ式です。

→いや、本当はいろいろと小難しい理論があるようですが、私はその域には達していないのでこのくらいの説明で勘弁してください。

Whereの例では、Whereの引数として抽出条件を指定する必要があるわけです。条件の指定方法はいくつか考えられますがVBのアプローチでは条件を判定する関数を作ってそれを指定するという手法を採用しました。

だから、Whereの引数には条件を判定する関数を指定する必要があります。たとえば、次のような関数をイメージしてください。

VB2008 対応

Private Function LargerThan4(ByVal animal As String) As Boolean

   
Return (Len(animal) >= 4)

End
Function

■リスト22

条件に合致するかどうかの判断はこの関数にまかせるというわけです。

実際、LINQからこの関数を呼び出すこともできることも思い出してください。

次のように書くのでした。

VB2008 対応


Dim
results = From animal In animals Where LargerThan4(animal)
 

■リスト23

これをメソッドベースの構文で書くと次のようになります。

VB2008 対応


Dim
results = animals.Where(AddressOf LargerThan4)
 

■リスト24

Whereメソッドの引数にLargerThan4関数を渡すわけです。ただし、普通にWhere(LargerThan4)と書くと、LargerThan4関数を呼び出して結果の戻り値をWhereの引数にするという意味になってしまいます。そして、引数がないという理由でこの呼び出しはエラーになります。

そうではなく、関数自体を引数として渡したいのでAddressOfを付ける必要があります。

関数の呼び出し結果ではなく、関数自体を引数として渡すこのAddressOfはVB6の頃からありました。ただ、VB6では関数を渡されても呼び出すことができなかったので一部の外部との連携処理以外では使用できなかったのですが、VB.NET(2002)以降はAddressOfで渡された関数を簡単に呼び出せるようになっています。

これをもう一歩進めたのがラムダ式です。上述のAddressOfによる呼び出しでは関数を別途定義しておいてそれを指定する必要がありますが、その定義も含めてここですべてを書いてしまおうというものです。

VB2008 対応


Dim
results = animals.Where(Function(animal) Len(animal) >= 4)
 

■リスト25

どうでしょうか?このように段階を追って見ていくとこのラムダ式の記述も理解できませんか?

 

拡張メソッドやらラムダ式で説明が長くなってしまいました。もう一度クエリ式の構文とメソッドベースの構文を振り返ります。

今度はさきほとの例に並び替え機能も追加して比較してみます。

クエリ式の構文では次のようになります。

VB2008 対応


Dim
results = From animal In animals Where Len(animal) >= 4 Order By animal.Last
 

■リスト26

この例は結果を最後の文字の順で並び替えます。最後の文字とは「アメンボ」なら「ボ」、「イノシシ」なら「シ」です。

これをメソッドベースの構文で書くと次のようになります。

VB2008 対応


Dim
results = animals.Where(Function(animal) Len(animal) >= 4).OrderBy(Function(animal) animal.Last)
 

■リスト27

メソッドベースの構文ではこのようにメソッドをどんどんつないで処理を記述していきます。よく文字列の前後のスペースをとったときの文字数を取得するのにValue.Trim.Lengthなどのようにメソッドをつなげて書きますがこれと同じ発想です。ただし、LINQの場合は遅延実行されるという特徴があります。

さらに、もう1つこの比較で注意していただきたいのはクエリ式の構文では「Order By」のようにスペースが1つ入っているのに、メソッドベースの構文ではスペースなしで「OrderBy」を書くことです。たったスペース1つの違いですが、このことは同じメソッドを呼び出している場合でもクエリ式の構文とメソッドベースの構文とでは名前が違う場合があるということを示しています。

だから、MSDNライブラリなどで調べるときもクエリ式の構文を使っているときはちゃんとクエリ式の構文のヘルプを見る必要があり、メソッドベースの構文を使っている場合はクラスライブラリのリファレンスを見る必要があるのです。

 

最後にAggregateの例を紹介します。Aggregateの集計関数はメソッドベースの構文の方がわかりやすくなります。

まずは、クエリ式の構文です。

VB2008 対応

'データソースの作成
Dim numbers() As Integer = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

'Aggregateで集計実行(即時実行)
Dim result = Aggregate number In numbers Into Sum()

'結果を表示
MsgBox(result) '55と表示される

■リスト28

これをメソッドベースの構文で書くと次のとおりです。

VB2008 対応

'データソースの作成
Dim numbers() As Integer = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

'Aggregateで集計実行(即時実行)
Dim result = numbers.Sum

'結果を表示
MsgBox(result) '55と表示される

■リスト29

どうですか?この場合はメソッドベースの構文の方が見かけも意味もすっきりすると思いませんか?