はじめに
初心者LINQerがLINQと戯れる : UniRx強化月間 - 渋谷ほととぎす通信
上記リンクの記事をリスペクトしつつ、コードを VB.NET に移植しながら LINQ を学んでいきます。
各コードで操作する配列
Dim nums() As Integer = New Integer() {1, -2, 3, 4, 5, 6, 7}
(例1)3 よりも大きい値の配列を結果として取得する
整数の配列 1, -2, 3, 4, 5, 6, 7
から 3
より大きい値を LINQ で抜き出します。4, 5, 6, 7
が期待される結果です。
リスペクト元記事同様に、最初にクエリ構文で書いてみます。
クエリ構文
' 操作の対象の配列を初期化する
Dim nums() As Integer = New Integer() {1, -2, 3, 4, 5, 6, 7}
' LINQ で 3 よりも大きい値の配列を結果として取得する
Dim linqResult As IEnumerable(Of Integer)
linqResult = From n As Integer In nums
Where n > 3
Select n
' 結果を表示するためのコード
Dim result As New StringBuilder
Dim comma As String = ""
With result
For Each n As Integer In linqResult
.Append(comma & n)
comma = ", "
Next
End With
System.Diagnostics.Debug.Print(result.ToString())
結果を表示するためのコードが鬼のように長い。何とかできないでしょうか。
' 操作の対象の配列を初期化する
Dim nums() As Integer = New Integer() {1, -2, 3, 4, 5, 6, 7}
' LINQ で 3 よりも大きい値の配列を結果として取得する
Dim linqResult As IEnumerable(Of String)
linqResult = From n As Integer In nums
Where n > 3
Select CStr(n)
' 結果を表示するためのコード
System.Diagnostics.Debug.Print(Join(linqResult.ToArray(), ", "))
なんとかはなりましたが、結果表示のためだけに linqResult の型まで変えてしまうのはサンプルとしては失格ではないでしょうか。以下のサンプルコードは結果を表示するためのコードは省略することにしますね。
また、操作したい元配列は全て同じなので、配列の初期化コードも省略することにします。
メソッド構文
次にメソッドチェーンと無名関数を組み合わせたメソッド構文で書いてみます。
Dim linqResult As IEnumerable(Of Integer)
linqResult = nums _
.Where(Function(n) n > 3) _
.Select(Function(n) n)
LINQ を調べると、C# のサンプルコードが多く見受けられます。それらを見ると C# ではメソッド構文が好まれている印象があります。しかしながら VB.NET でメソッド構文を用いるとやや冗長な感じがして、クエリ構文の方がすっきりしています。
自分としては SQL に似たクエリ構文の方が、圧倒的になじみのあり、また VB.NET 上ですっきり書けるので好ましいです。逆に SQL になじみがなかったり C# を使っている方はメソッド構文を好むのでしょうね。C# のサンプル構文にメソッド構文が多い理由が解る気がします。
(例2)3 より大きく、且つ、2 で割り切れる値の配列を取得する
結果として 4, 6
を期待するコードです。
クエリ構文
Dim linqResult As IEnumerable(Of Integer)
linqResult = From n As Integer In nums
Where 1 = 1 _
And n > 3 _
And n Mod 2 = 0
Select n
Where 句に条件を増やすことになりますが、私は SQL では最初に 1=1
して AND
を揃えて書くのが好きです。上記サンプルもそのように書いてみました。
しかし LINQ では Where 句を二行に分けることも出来ます。
Dim linqResult As IEnumerable(Of Integer)
linqResult = From n As Integer In nums
Where n > 3
Where n Mod 2 = 0
Select n
SQL から考えるとへんな気がしますが、行の継続記号を書かなくて済むし、1=1
しなくても条件のインデントが揃うので、こちらの方が好きです。
メソッド構文
メソッド構文も二通りのサンプルを示します。
Dim linqResult As IEnumerable(Of Integer)
linqResult = nums _
.Where(Function(n) n > 3 And n Mod 2 = 0) _
.Select(Function(n) n)
Dim linqResult As IEnumerable(Of Integer)
linqResult = nums _
.Where(Function(n) n > 3) _
.Where(Function(n) n Mod 2 = 0) _
.Select(Function(n) n)
(例3)3より大きい配列の各要素に文字列 "piyo" を付与した配列を取得する
4piyo, 5piyo, 6piyo, 7piyo
のような結果を期待します。
返値が整数ではなく文字列になるので結果出力のためのコードも含めます。
クエリ構文
Dim linqResult As IEnumerable(Of String)
linqResult = From n As Integer In nums
Where n > 3
Select n & "piyo"
System.Diagnostics.Debug.Print(Join(linqResult.ToArray(), ", "))
メソッド構文
linqResult = nums _
.Where(Function(n) n > 3) _
.Select(Function(n) n & "piyo")
System.Diagnostics.Debug.Print(Join(linqResult.ToArray(), ", "))
(例4)異なる型が混在する配列から String 型だけを抽出する
クエリ構文
Dim mix() As Object = New Object() {"hoge", "piyo", 123, "foo", 456}
Dim linqResult As IEnumerable(Of Object)
linqResult = From el As Object In mix.OfType(Of String)
Select el
System.Diagnostics.Debug.Print(Join(linqResult.ToArray(), ", "))
メソッド構文
Dim mix() As Object = New Object() {"hoge", "piyo", 123, "foo", 456}
Dim linqResult As IEnumerable(Of Object)
linqResult = mix.OfType(Of String) _
.Select(Function(el) el)
System.Diagnostics.Debug.Print(Join(linqResult.ToArray(), ", "))
(例5)匿名型で受け取る
リスペクト記事ではこれがどのような結果を期待するのかわからないため、はずしている可能性が大きいですが。
クエリ構文
Dim nums() As Integer = New Integer() {1, -2, 3, 4, 5, 6, 7}
Dim linqResult = From n As Object In nums
Where n > 5
Select CObj(n)
' 結果を表示するためのコード
Dim result As New StringBuilder
Dim comma As String = ""
With result
For Each n As String In linqResult
.Append(comma & n)
comma = ", "
Next
End With
System.Diagnostics.Debug.Print(result.ToString())
メソッド構文
Dim nums() As Integer = New Integer() {1, -2, 3, 4, 5, 6, 7}
Dim linqResult = nums _
.Where(Function(n) n > 5) _
.Select(Function(n) CObj(n))
' 結果を表示するためのコード
Dim result As New StringBuilder
Dim comma As String = ""
With result
For Each n As String In linqResult
.Append(comma & n)
comma = ", "
Next
End With
System.Diagnostics.Debug.Print(result.ToString())
(例2.2)2番目以降の要素で且つ、3より大きく且つ、2で割り切れる値の配列を取得する
メソッド構文
こればかりはクエリ構文での書き方が解りませんでした。
Dim linqResult = nums _
.Where(Function(n, index) index > 2 And n > 3 And n Mod 2 = 0) _
.Select(Function(n) n)
おわりに
例3以外は Select
を省略しても良いようです。
VB.NET では行継続記号を書かなくて済むクエリ構文の方がすっきりして書きやすい気がします。