Lambda 表达式 Lambda Expressions (Visual Basic)
Lambda 表达式是一个不带名字的函数(function)或子过程(sub),可以用在任何接收委托的地方。Lambda 表达式可以是函数,也可以是子过程,可以是单行的,也可以写成多行。您可以把当前范围的值传递给 lambda 表达式。
注意,RemoveHandler 语句是一个例外,您不可以给委托参数传入一个 lambda 表达式
您使用 Function 或 Sub 关键字创建 lambda 表达式,就像创建普通函数和子过程一样。不过,lambda 表达式包含在一个语句中。
下面例子是一个 lambda 表达式,将参数自增 1 后返回。此例子展示单行和多行的 function 式 lambda 表达式语法
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 writeline2 = Sub(x)
Console.WriteLine(x)
End Sub
' Write "Hello".
writeline1("Hello")
' Write "World"
writeline2("World")
注意到前面的例子中,lambda 表达式赋值给了变量。当您引用该变量时,就调用该 lambda 表达式。您还可以声明 lambda 表达式的同时调用它,看下面的例子。
lambda 表达式可以作为函数的返回值(例子在本文后面的 上下文章节 中),或者作为实参传送给委托类型的参数,像下面列子一样。
Module Module2
Sub Main()
' 下面代码会打印 Success, 因为 4 是偶数.
testResult(4, Function(num) num Mod 2 = 0)
' 下面代码会打印 Failure, 因为 5 不大于 10.
testResult(5, Function(num) num > 10)
End Sub
' Sub testResult 接收两个参数,一个 integer 和一个 function 委托,该委托接收 integer 值返回 boolean 值。
' 如果函数接收 integer 的实参并返回 True,则输出 Success,否则输出 Failure。
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 表达式没有修饰符(modifier),如 Overloads 或 Overrides
- 单行 lambda 函数的返回值类型不使用 As 子句标识,而是从 lambda 表达式主体计算所得值中推断。例如,lambda 表达式的主体是 cust.City = "London",则其返回值类型是 Boolean。
- 多行 lambda 函数,可以使用 As 子句标识返回类型,或者省略掉 As 子句而自动推断返回类型。当多行 lambda 函数省略掉 As 子句,返回值的类型推断为所有 Return 子句的主导类型。主导类型是数组文本中所有其它类型可以扩大到的唯一类型。如果这个唯一类型不能决定,则主导类型是 Object。例如,返回值列表提供给数组文本包含的值类型有 Integer、Long、Double,则最终数组是 Double 类型。Integer 和 Long 扩大为 Double,且唯一是 Double。这样,Double 就是主导类型。查看详细,请看 Widening and Narrowing Conversions。
- 单行函数的主体必须是一个有返回值的表达式,而不是一个语句。单行函数没有 Return 语句,其返回值是函数体中表达式的值。
- 单行子过程的主体必须是一个单行语句。
- 单行函数或子过程没有 End Function 或 End Sub 语句。
- 您可以用 As 关键字标识 lambda 表达式参数的类型,或者自动推断该类型。要么所有参数类型都标识,要是所有都自动推断。
- Optional 和 Paramarray 参数不被允许
- 泛型参数不被允许
上下文
lambda 表达式跟定义它的上下文共享作用范围。它跟容纳范围(containing scope)的内部的其它代码有相同的访问权限,包括访问成员变量、函数或子过程、Me、参数、和本地变量。
访问容纳范围(containing scope)的本地变量和参数可以超出该范围的生命期。只要委托引用的 lambda 表达式不被垃圾收集器收集,就可以访问保持的原始环境的变量。在下面的例子中,变量 target 是 makeTheGame 的本地变量,lambda 表达式 playTheGame 定义在 makeTheGame 中。注意到作为返回值的 lambda 表达式,在 Main子过程中赋值给 takeAGuess,仍然可以访问本地变量 target。
Sub Main()
'变量 takeAGuess 是一个 Boolean 函数,它存储在 makeTheGame 中设置的目标数字
Dim takeAGuess As gameDelegate = makeTheGame()
' 设置循环来玩游戏
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"))
' 输入 0 表示退出游戏
If guess = 0 Then
gameOver = True
Else
' 测试您的猜测,并告知您是否正确。takeAGuess 方法因不同的猜测值而被多次调用
' Main 过程访问不了目标值,且它没有被传入。
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
' 生成目标数字,介于 1 和 10。注意到 target 是一个本地变量,
' 当从 makeTheGame 返回后,它就不能被直接访问了。
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)
' 本游戏返回 lambda 表达式,该 lambda 表达式承载了创建它的上下文的环境,
' 这个环境包含 target 变量(target number)。注意到仅有当前猜测(current guess)
' 才是返回 lambda 表达式的参数,而不是 target。
' 猜测值跟目标值是否匹配
Dim playTheGame = Function(guess As Integer) guess = target
Return playTheGame
End Function
End Module
下面例子演示嵌套 lambda 表达式的大范围访问权。当作为返回值的 lambda 表达式在 Main 中被调用(aDel),它访问这些元素。
- 定义它的类的字段:aField
- 定义它的类的属性:aProp
- 定义它的方法 functionWithNestedLambda 的参数: level1
- functionWithNestedLambda 的本地变量:lovalVar
- 外部 lambda 表达式的参数:level2
Sub Main()
' 创建类的一个实例,属性值 Prop 设为 1
Dim lambdaScopeDemoInstance =
New LambdaScopeDemoClass With {.Prop = 1}
' 变量 aDel 被绑定到 functionWithNestedLambda 返回的嵌套的 lambda 表达式
Dim aDel As aDelegate =
lambdaScopeDemoInstance.functionWithNestedLambda(2)
' 现在返回的嵌套的 lambda 表达式被调用,4 变成参数 level3 的值
Console.WriteLine("First value returned by aDel: " & aDel(4))
' 改变某些值,以验证 lambda 表达式是否可访问变量,不仅他们的原始值
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
' 当嵌套的 lambda 表达式作为 Main 的 aDel 第一次运行,变量有这些值:
' level1 = 2
' level2 = 3, Return 语句中,调用 aLambda 后
' level3 = 4, aDel 在 Main 中调用后
' locarVar = 5
' aField = 6
' aProp = 1
' 第二次执行, 更改了两个变量:
' aField = 20
' aProp = 30
' level3 = 40
Dim aLambda = Function(level2 As Integer) _
Function(level3 As Integer) _
level1 + level2 + level3 + localVar + aField + aProp
'函数返回嵌套的 lambda,3 作为参数 level2 的值
Return aLambda(3)
End Function
End Class
End Module
转换到委托类型
一个 lambda 表达式可以隐式地转换为兼容的委托类型。有关兼容性的普遍问题(general requirements for compatibility),请看 Relaxed Delegate Conversion。例如,下面代码展示一个 lambda 表达式隐式转换为匹配的委托签名 Func(Of Integer, Boolean)。
Delegate Function MultipleOfTen(ByVal num As Integer) As Boolean
' 这个函数匹配委托
Function IsMultipleOfTen(ByVal num As Integer) As Boolean
Return num Mod 10 = 0
End Function
' 这个方法接收一个委托类型的输入参数,checkDelegate 参数也可以是类型 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
' 这个方法演示显式地定义的委托(explicitly defined delegate)和 lambda 表达式传给同样的输入参数。
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
下面代码演示 lambda 表达式隐式地转换为匹配的委托签名 Sub(Of Double, String, Double)。
Delegate Sub StoreCalculation(ByVal value As Double,
ByVal calcType As String,
ByVal result As Double)
Sub Main()
' 创建 DataTable 存储信息
Dim valuesTable = New DataTable("Calculations")
valuesTable.Columns.Add("Value", GetType(Double))
valuesTable.Columns.Add("Calculation", GetType(String))
valuesTable.Columns.Add("Result", GetType(Double))
' 定义 lambda 子过程写信息到 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
' 定义原值
Dim s = {1, 2, 3, 4, 5, 6, 7, 8, 9}
' 执行计算
Array.ForEach(s, Sub(c) CalculateSquare(c, writeToValuesTable))
Array.ForEach(s, Sub(c) CalculateSquareRoot(c, writeToValuesTable))
' 显示数据
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 表达式,如果 nullable 参数被赋值则返回 True,否则返回 False。
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 lastIndex =
Function(intArray() As Integer) intArray.Length - 1
For i = 0 To lastIndex(numbers)
numbers(i) += 1
Next
原文:http://msdn.microsoft.com/en-us/library/bb531253(v=VS.100).aspx