方法: LINQ クエリのカスタム メソッドを追加する (Visual Basic)
IEnumerable<T> インターフェイスに拡張メソッドを追加することで、LINQ クエリに使用するメソッド セットを拡張します。 たとえば、一連の値から単一の値を計算するために、平均や最大を求める標準的な演算に加えて、カスタムの集計メソッドを作成します。 また、一連の値を受け取って新しい一連の値を返す特定のデータ変換やカスタム フィルターとして動作するメソッドも作成します。 このようなメソッドには、Distinct、Skip、Reverse があります。
IEnumerable<T> インターフェイスを拡張すると、列挙可能なコレクションにカスタム メソッドを適用できます。 詳細については、「拡張メソッド」を参照してください。
集計メソッドを追加する
集計メソッドは、一連の値から単一の値を計算するメソッドです。 LINQ は、Average、Min、Max などの集計メソッドを提供します。 IEnumerable<T> インターフェイスに拡張メソッドを追加することで、独自の集計メソッドを作成できます。
次のコード例は、double
型の一連の数値から中央値を求める Median
という拡張メソッドの作成方法を示しています。
Imports System.Runtime.CompilerServices
Module LINQExtension
' Extension method for the IEnumerable(of T) interface.
' The method accepts only values of the Double type.
<Extension()>
Function Median(ByVal source As IEnumerable(Of Double)) As Double
If Not source.Any() Then
Throw New InvalidOperationException("Cannot compute median for an empty set.")
End If
Dim sortedSource = (From number In source
Order By number).ToList()
Dim itemIndex = sortedSource.Count \ 2
If sortedSource.Count Mod 2 = 0 Then
' Even number of items in list.
Return (sortedSource(itemIndex) + sortedSource(itemIndex - 1)) / 2
Else
' Odd number of items in list.
Return sortedSource(itemIndex)
End If
End Function
End Module
この拡張メソッドは、IEnumerable<T> インターフェイスにある他の集計メソッドを呼び出すときと同じように、列挙可能な任意のコレクションに対して呼び出すことができます。
Note
Visual Basic では、Aggregate
句または Group By
句の代わりに、メソッド呼び出しまたは標準クエリ構文を使用できます。 詳細については、「Aggregate 句」および「Group By 句」を参照してください。
double
型の配列に対して Median
メソッドを使用する方法を次のコード例に示します。
Dim numbers() As Double = {1.9, 2, 8, 4, 5.7, 6, 7.2, 0}
Dim query = Aggregate num In numbers Into Median()
Console.WriteLine("Double: Median = " & query)
' This code produces the following output:
'
' Double: Median = 4.85
さまざまな型を受け取るために集計メソッドをオーバーロードする
集計メソッドでさまざまな型を受け取るように、集計メソッドをオーバーロードすることができます。 その標準的な方法として、型ごとにオーバーロードを作成します。 または、ジェネリック型を受け取り、デリゲートを使って特定の型に変換するオーバーロードを作成する方法もあります。 その両方の方法を組み合わせることもできます。
型ごとにオーバーロードを作成するには
サポート予定の型ごとに固有のオーバーロードを作成できます。 次のコード例では、integer
型の Median
メソッドのオーバーロードを示します。
' Integer overload
<Extension()>
Function Median(ByVal source As IEnumerable(Of Integer)) As Double
Return Aggregate num In source Select CDbl(num) Into med = Median()
End Function
これで、次のコードに示すように、integer
型と double
型の両方に対して、Median
のオーバーロードを呼び出すことができるようになりました。
Dim numbers1() As Double = {1.9, 2, 8, 4, 5.7, 6, 7.2, 0}
Dim query1 = Aggregate num In numbers1 Into Median()
Console.WriteLine("Double: Median = " & query1)
Dim numbers2() As Integer = {1, 2, 3, 4, 5}
Dim query2 = Aggregate num In numbers2 Into Median()
Console.WriteLine("Integer: Median = " & query2)
' This code produces the following output:
'
' Double: Median = 4.85
' Integer: Median = 3
ジェネリック オーバーロードを作成するには
一連のジェネリック オブジェクトを受け取るオーバーロードを作成することもできます。 このオーバーロードは、デリゲートをパラメーターとして受け取り、ジェネリック型の一連のオブジェクトを特定の型に変換します。
次のコードは、Func<T,TResult> デリゲートをパラメーターとして受け取る Median
メソッドのオーバーロードを示しています。 このデリゲートは、ジェネリック型 T
のオブジェクトを受け取り、double
型のオブジェクトを返します。
' Generic overload.
<Extension()>
Function Median(Of T)(ByVal source As IEnumerable(Of T),
ByVal selector As Func(Of T, Double)) As Double
Return Aggregate num In source Select selector(num) Into med = Median()
End Function
これで、任意の型の一連のオブジェクトに対して Median
メソッドを呼び出すことができます。 型に固有のメソッド オーバーロードがない場合は、デリゲート パラメーターを渡す必要があります。 この目的で、Visual Basic ではラムダ式を使用できます。 また、メソッド呼び出しの代わりに Aggregate
句または Group By
句を使用した場合、その句のスコープにある任意の値または式を渡すことができます。
次のコード例では、整数の配列と文字列の配列に対して Median
メソッドを呼び出す方法を示します。 文字列の場合は、配列に格納されている文字列の長さの中央値が計算されます。 この例は、それぞれのケースについて、Median
メソッドに Func<T,TResult> デリゲート パラメーターを渡す方法を示しています。
Dim numbers3() As Integer = {1, 2, 3, 4, 5}
' You can use num as a parameter for the Median method
' so that the compiler will implicitly convert its value to double.
' If there is no implicit conversion, the compiler will
' display an error message.
Dim query3 = Aggregate num In numbers3 Into Median(num)
Console.WriteLine("Integer: Median = " & query3)
Dim numbers4() As String = {"one", "two", "three", "four", "five"}
' With the generic overload, you can also use numeric properties of objects.
Dim query4 = Aggregate str In numbers4 Into Median(str.Length)
Console.WriteLine("String: Median = " & query4)
' This code produces the following output:
'
' Integer: Median = 3
' String: Median = 4
コレクションを返すメソッドを追加する
IEnumerable<T> インターフェイスは、一連の値を返すカスタム クエリ メソッドを追加することで拡張できます。 その場合、メソッドで型 IEnumerable<T> のコレクションを返す必要があります。 このようなメソッドを使用すると、一連の値にフィルターまたはデータ変換を適用することができます。
次の例では、コレクション内の最初の要素から 1 つおきに要素を返す AlternateElements
という名前の拡張メソッドを作成する方法を示しています。
' Extension method for the IEnumerable(of T) interface.
' The method returns every other element of a sequence.
<Extension()>
Iterator Function AlternateElements(Of T)(
ByVal source As IEnumerable(Of T)
) As IEnumerable(Of T)
Dim i = 0
For Each element In source
If (i Mod 2 = 0) Then
Yield element
End If
i = i + 1
Next
End Function
この拡張メソッドは、次のコードに示すとおり、IEnumerable<T> インターフェイスにある他のメソッドを呼び出すときと同じように、列挙可能な任意のコレクションに対して呼び出すことができます。
Dim strings() As String = {"a", "b", "c", "d", "e"}
Dim query5 = strings.AlternateElements()
For Each element In query5
Console.WriteLine(element)
Next
' This code produces the following output:
'
' a
' c
' e
関連項目
.NET