Lambda 表达式 (Visual Basic)
“Lambda 表达式”是一种没有名称的函数或子例程,可在委托有效的任何地方使用。 Lambda 表达式可以是函数或子例程,且可为单行或多行。 可以将值从当前范围传递到 Lambda 表达式。
备注
RemoveHandler 语句是一个例外。不能为 RemoveHandler 的委托参数传递 lambda 表达式。
使用 Function 或 Sub 关键字可以创建 Lambda 表达式,就像创建标准函数或子例程一样。 但是,Lambda 表达式包括在语句中。
下面的示例是一个 lambda 表达式,该表达式递增其参数并返回值。 该示例同时显示了一个函数的单行和多行 Lambda 表达式语法。
Dim increment1 = Function(x) x + 1
Dim increment2 = Function(x)
Return x + 2
End Function
' Write the value 2.
Console.WriteLine(increment1(1))
' Write the value 4.
Console.WriteLine(increment2(2))
下面的示例是将值写入控制台的 Lambda 表达式。 该示例同时显示了一个子例程的单行和多行 Lambda 表达式语法。
Dim writeline1 = Sub(x) Console.WriteLine(x)
Dim writeline2 = Sub(x)
Console.WriteLine(x)
End Sub
' Write "Hello".
writeline1("Hello")
' Write "World"
writeline2("World")
请注意,在前面的示例中,为一个变量名称分配了 Lambda 表达式。 只要引用该变量,就会调用 Lambda 表达式。 还可以同时声明和调用 Lambda 表达式,如下面的示例所示。
Console.WriteLine((Function(num As Integer) num + 1)(5))
Lambda 表达式可以作为函数调用的值返回(如本主题后面上下文部分中的示例所示),或作为实参传递给会获取委托类型的形参,如下面的示例所示。
Module Module2
Sub Main()
' The following line will print Success, because 4 is even.
testResult(4, Function(num) num Mod 2 = 0)
' The following line will print Failure, because 5 is not > 10.
testResult(5, Function(num) num > 10)
End Sub
' Sub testResult takes two arguments, an integer value and a
' delegate function that takes an integer as input and returns
' a boolean.
' If the function returns True for the integer argument, Success
' is displayed.
' If the function returns False for the integer argument, Failure
' is displayed.
Sub testResult(ByVal value As Integer, ByVal fun As Func(Of Integer, Boolean))
If fun(value) Then
Console.WriteLine("Success")
Else
Console.WriteLine("Failure")
End If
End Sub
End Module
Lambda 表达式语法
Lambda 表达式的语法类似于标准函数或子例程的语法。 区别如下:
lambda 表达式没有名称。
Lambda 表达式不能有修饰符,例如 Overloads 或 Overrides。
单行 Lambda 函数不使用 As 子句来指定返回类型。 相反,类型是从 lambda 表达式主体计算得出的值推断而来的。 例如,如果 lambda 表达式的主体为 cust.City = "London",则其返回类型为 Boolean。
在多行 Lambda 函数中,可以使用 As 子句指定返回类型,或者省略 As 子句以便推断返回类型。 省略某多行 Lambda 函数的 As 子句时,返回类型被推断为该多行 Lambda 函数中所有 Return 语句的主导类型。 主导类型是唯一可以扩大到的所有其他类型的类型。 如果无法确定此唯一类型,则主导类型是数组中所有其他类型都可以收缩到的唯一类型。 如果这两种唯一类型都无法确定,则主导类型是 Object。 在这种情况下,如果 Option Strict 设置为 On,则会发生编译器错误。
例如,如果表达式提供给Return语句包含值类型的Integer, Long,和Double,则生成的数组类型不Double。 Integer 和 Long 都扩大到 Double 且仅扩大到 Double。 因此,Double 是主导类型。 有关更多信息,请参见扩大转换和收缩转换 (Visual Basic)。
单行函数的主体必须是返回一个值的表达式,而不是语句。 单行函数没有 Return 语句。 单行函数返回的值是函数主体中的表达式的值。
单行子例程的主体必须是单行语句。
单行函数和子例程不包括 End Function 或 End Sub 语句。
可以使用 As 关键字指定 Lambda 表达式参数的数据类型,也可以推断参数的数据类型。 要么所有参数都必须具有指定的数据类型,要么必须推断所有类型。
不允许使用 Optional 和 Paramarray 参数。
不允许使用泛型参数。
异步 Lambda
您可以轻松地创建的 lambda 表达式或语句使用合并异步处理的Async (Visual Basic)和Await 运算符 (Visual Basic)关键字。 例如,下面的 Windows 窗体示例包含一个事件处理程序调用,并等待异步方法, ExampleMethodAsync。
Public Class Form1
Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
' ExampleMethodAsync returns a Task.
Await ExampleMethodAsync()
TextBox1.Text = vbCrLf & "Control returned to button1_Click."
End Sub
Async Function ExampleMethodAsync() As Task
' The following line simulates a task-returning asynchronous process.
Await Task.Delay(1000)
End Function
End Class
您可以通过使用在异步 lambda 添加相同的事件处理程序AddHandler 语句。 若要将此处理程序,添加Async之前的 lambda 参数列表,如以下示例所示的修饰符。
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
AddHandler Button1.Click,
Async Sub(sender1, e1)
' ExampleMethodAsync returns a Task.
Await ExampleMethodAsync()
TextBox1.Text = vbCrLf & "Control returned to Button1_ Click."
End Sub
End Sub
Async Function ExampleMethodAsync() As Task
' The following line simulates a task-returning asynchronous process.
Await Task.Delay(1000)
End Function
End Class
有关如何创建和使用异步方法的详细信息,请参阅使用 Async 和 Await 的异步编程(C# 和 Visual Basic)。
上下文
Lambda 表达式与它被定义时所在的范围共享其上下文。 它与在包含范围中编写的任何代码具有相同的访问权限。 这包括访问包含范围中的成员变量、函数和子函数、Me 以及参数和局部变量的权限。
对包含范围中的局部变量和参数的访问可以超出该范围的生存期。 只要引用 lambda 表达式的委托不能进行垃圾回收,就将保留对原始环境中的变量的访问。 在下面的示例中,变量 target 对于 makeTheGame 方法(lambda 表达式 playTheGame 在该方法中定义)是局部变量。 请注意,返回并分配给 Main 中 takeAGuess 的 lambda 表达式仍然具有访问局部变量 target 的权限。
Module Module6
Sub Main()
' Variable takeAGuess is a Boolean function. It stores the target
' number that is set in makeTheGame.
Dim takeAGuess As gameDelegate = makeTheGame()
' Set up the loop to play the game.
Dim guess As Integer
Dim gameOver = False
While Not gameOver
guess = CInt(InputBox("Enter a number between 1 and 10 (0 to quit)", "Guessing Game", "0"))
' A guess of 0 means you want to give up.
If guess = 0 Then
gameOver = True
Else
' Tests your guess and announces whether you are correct. Method takeAGuess
' is called multiple times with different guesses. The target value is not
' accessible from Main and is not passed in.
gameOver = takeAGuess(guess)
Console.WriteLine("Guess of " & guess & " is " & gameOver)
End If
End While
End Sub
Delegate Function gameDelegate(ByVal aGuess As Integer) As Boolean
Public Function makeTheGame() As gameDelegate
' Generate the target number, between 1 and 10. Notice that
' target is a local variable. After you return from makeTheGame,
' it is not directly accessible.
Randomize()
Dim target As Integer = CInt(Int(10 * Rnd() + 1))
' Print the answer if you want to be sure the game is not cheating
' by changing the target at each guess.
Console.WriteLine("(Peeking at the answer) The target is " & target)
' The game is returned as a lambda expression. The lambda expression
' carries with it the environment in which it was created. This
' environment includes the target number. Note that only the current
' guess is a parameter to the returned lambda expression, not the target.
' Does the guess equal the target?
Dim playTheGame = Function(guess As Integer) guess = target
Return playTheGame
End Function
End Module
下面的示例演示嵌套 lambda 表达式所具有的宽广的访问权限范围。 在 Main 中将返回的 lambda 表达式作为 aDel 执行时,该表达式能够访问下列元素:
在其中定义该表达式的类的字段:aField
在其中定义该表达式的类的属性:aProp
在其中定义该表达式的方法 functionWithNestedLambda 的参数:level1
functionWithNestedLambda 的局部变量:localVar
在其中嵌套该表达式的 lambda 表达式的参数:level2
Module Module3
Sub Main()
' Create an instance of the class, with 1 as the value of
' the property.
Dim lambdaScopeDemoInstance =
New LambdaScopeDemoClass With {.Prop = 1}
' Variable aDel will be bound to the nested lambda expression
' returned by the call to functionWithNestedLambda.
' The value 2 is sent in for parameter level1.
Dim aDel As aDelegate =
lambdaScopeDemoInstance.functionWithNestedLambda(2)
' Now the returned lambda expression is called, with 4 as the
' value of parameter level3.
Console.WriteLine("First value returned by aDel: " & aDel(4))
' Change a few values to verify that the lambda expression has
' access to the variables, not just their original values.
lambdaScopeDemoInstance.aField = 20
lambdaScopeDemoInstance.Prop = 30
Console.WriteLine("Second value returned by aDel: " & aDel(40))
End Sub
Delegate Function aDelegate(
ByVal delParameter As Integer) As Integer
Public Class LambdaScopeDemoClass
Public aField As Integer = 6
Dim aProp As Integer
Property Prop() As Integer
Get
Return aProp
End Get
Set(ByVal value As Integer)
aProp = value
End Set
End Property
Public Function functionWithNestedLambda(
ByVal level1 As Integer) As aDelegate
Dim localVar As Integer = 5
' When the nested lambda expression is executed the first
' time, as aDel from Main, the variables have these values:
' level1 = 2
' level2 = 3, after aLambda is called in the Return statement
' level3 = 4, after aDel is called in Main
' locarVar = 5
' aField = 6
' aProp = 1
' The second time it is executed, two values have changed:
' aField = 20
' aProp = 30
' level3 = 40
Dim aLambda = Function(level2 As Integer) _
Function(level3 As Integer) _
level1 + level2 + level3 + localVar +
aField + aProp
' The function returns the nested lambda, with 3 as the
' value of parameter level2.
Return aLambda(3)
End Function
End Class
End Module
转换为委托类型
可以将 lambda 表达式隐式转换为兼容的委托类型。 有关兼容性的一般要求的信息,请参见宽松委托转换 (Visual Basic)。 例如,下面的代码示例演示一个隐式转换为 Func(Of Integer, Boolean) 或匹配委托签名的 Lambda 表达式。
' Explicitly specify a delegate type.
Delegate Function MultipleOfTen(ByVal num As Integer) As Boolean
' This function matches the delegate type.
Function IsMultipleOfTen(ByVal num As Integer) As Boolean
Return num Mod 10 = 0
End Function
' This method takes an input parameter of the delegate type.
' The checkDelegate parameter could also be of
' type Func(Of Integer, Boolean).
Sub CheckForMultipleOfTen(ByVal values As Integer(),
ByRef checkDelegate As MultipleOfTen)
For Each value In values
If checkDelegate(value) Then
Console.WriteLine(value & " is a multiple of ten.")
Else
Console.WriteLine(value & " is not a multiple of ten.")
End If
Next
End Sub
' This method shows both an explicitly defined delegate and a
' lambda expression passed to the same input parameter.
Sub CheckValues()
Dim values = {5, 10, 11, 20, 40, 30, 100, 3}
CheckForMultipleOfTen(values, AddressOf IsMultipleOfTen)
CheckForMultipleOfTen(values, Function(num) num Mod 10 = 0)
End Sub
下面的代码示例演示一个隐式转换为 Sub(Of Double, String, Double) 或匹配委托签名的 Lambda 表达式。
Module Module1
Delegate Sub StoreCalculation(ByVal value As Double,
ByVal calcType As String,
ByVal result As Double)
Sub Main()
' Create a DataTable to store the data.
Dim valuesTable = New DataTable("Calculations")
valuesTable.Columns.Add("Value", GetType(Double))
valuesTable.Columns.Add("Calculation", GetType(String))
valuesTable.Columns.Add("Result", GetType(Double))
' Define a lambda subroutine to write to the DataTable.
Dim writeToValuesTable = Sub(value As Double, calcType As String, result As Double)
Dim row = valuesTable.NewRow()
row(0) = value
row(1) = calcType
row(2) = result
valuesTable.Rows.Add(row)
End Sub
' Define the source values.
Dim s = {1, 2, 3, 4, 5, 6, 7, 8, 9}
' Perform the calculations.
Array.ForEach(s, Sub(c) CalculateSquare(c, writeToValuesTable))
Array.ForEach(s, Sub(c) CalculateSquareRoot(c, writeToValuesTable))
' Display the data.
Console.WriteLine("Value" & vbTab & "Calculation" & vbTab & "Result")
For Each row As DataRow In valuesTable.Rows
Console.WriteLine(row(0).ToString() & vbTab &
row(1).ToString() & vbTab &
row(2).ToString())
Next
End Sub
Sub CalculateSquare(ByVal number As Double, ByVal writeTo As StoreCalculation)
writeTo(number, "Square ", number ^ 2)
End Sub
Sub CalculateSquareRoot(ByVal number As Double, ByVal writeTo As StoreCalculation)
writeTo(number, "Square Root", Math.Sqrt(number))
End Sub
End Module
在将 Lambda 表达式分配给委托或将其作为实参传递到过程时,可以指定形参名称,但请省略其数据类型,以便从委托中获取类型。
示例
下面的示例定义了一个 lambda 表达式,如果可以为 null 的参数已被赋值,则该表达式返回 True;如果该参数的值为 Nothing,则返回 False。
Dim notNothing = Function(num? As Integer) num IsNot Nothing Dim arg As Integer = 14 Console.WriteLine("Does the argument have an assigned value?") Console.WriteLine(notNothing(arg))
下面的示例定义了一个 lambda 表达式,该表达式返回数组中最后一个元素的索引。
Dim numbers() = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} Dim lastIndex = Function(intArray() As Integer) intArray.Length - 1 For i = 0 To lastIndex(numbers) numbers(i) += 1 Next
请参见
任务
如何:创建 Lambda 表达式 (Visual Basic)