如何:为 LINQ 查询添加自定义方法(Visual Basic)

通过将扩展方法添加到 IEnumerable<T> 接口来扩展用于 LINQ 查询的方法集。 例如,除了标准平均值或最大作之外,还可以创建自定义聚合方法,以从值序列中计算单个值。 还可以创建一个方法,该方法用作自定义筛选器或值序列的特定数据转换,并返回一个新序列。 此类方法的示例包括DistinctSkipReverse

扩展 IEnumerable<T> 接口时,可以将自定义方法应用于任何可枚举集合。 有关详细信息,请参阅扩展方法

添加聚合方法

聚合方法从一组值计算单个值。 LINQ 提供了多种聚合方法,包括AverageMinMax。 可以通过向 IEnumerable<T> 接口添加扩展方法来创建自己的聚合方法。

下面的代码示例演示如何创建一个扩展 Median 方法,以计算类型 double序列的中值。

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> 接口调用其他聚合方法一样,对任何可枚举集合进行调用。

注释

在 Visual Basic 中,您可以为AggregateGroup By子句使用方法调用或标准查询语法。 有关详细信息,请参阅 聚合子句Group By 子句

下面的代码示例演示如何对类型的Median数组使用double该方法。

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

重载聚合方法以接受各种类型

可以重载聚合方法,以便它接受各种类型的序列。 标准方法是为每个类型创建重载。 另一种方法是创建一个重载函数,该函数通过使用委托将泛型类型转换为特定类型。 还可以合并这两种方法。

为每种类型创建重载

可以为要支持的每个类型创建特定的重载。 下面的代码示例演示Median类型的integer方法重载。

' 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

现在便可以为 Medianinteger 类型调用 double 重载了,如以下代码中所示:

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

创建一般重载

还可以创建接受泛型对象序列的重载。 此重载采用委托作为参数,并使用它将泛型类型的对象序列转换为特定类型。

下面的代码展示 Median 方法的重载,该重载将 Func<T,TResult> 委托作为参数。 此委托采用泛型类型 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 中,可以使用 lambda 表达式实现此目的。 此外,如果使用 AggregateGroup By 子句而不是方法调用,则可以传递此子句范围内的任何值或表达式。

以下示例代码演示如何调用 Median 整数数组和字符串数组的方法。 对于字符串,计算数组中字符串长度的中值。 该示例演示如何将 Func<T,TResult> 委托参数传递给 Median 每个事例的方法。

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> 的集合。 此类方法可用于将筛选器或数据转换应用于值序列。

以下示例演示如何创建一个从第一个元素开始返回集合中所有其他元素的扩展 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

另请参阅