Lambda 表达式 Lambda Expressions (Visual Basic)
Lambda 表达式是一个不带名字的函数(function)或子过程(sub),可以用在任何接收委托的地方。Lambda 表达式可以是函数,也可以是子过程,可以是单行的,也可以写成多行。您可以把当前范围的值传递给 lambda 表达式。
注意,RemoveHandler 语句是一个例外,您不可以给委托参数传入一个 lambda 表达式
您使用 Function 或 Sub 关键字创建 lambda 表达式,就像创建普通函数和子过程一样。不过,lambda 表达式包含在一个语句中。
下面例子是一个 lambda 表达式,将参数自增 1 后返回。此例子展示单行和多行的 function 式 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()
'
下面代码会打印 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。
Module Module6
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
Module Module3
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)。
Module Module1
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。
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
原文:http://msdn.microsoft.com/en-us/library/bb531253(v=VS.100).aspx