【vs.net 2008系列】 8.lambda 表达式
“lambda 表达式”是一种无名函数,用于计算并返回单个值。lambda 表达式可在委托类型有效的任何地方使用。
说明: |
|---|
|
RemoveHandler 语句是一个例外。不能为 RemoveHandler 的委托参数传递 lambda 表达式。 |
下面的示例是一个 lambda 表达式,该表达式递增其参数并返回值。
Function (num As Integer) num + 1
由于 lambda 表达式仅仅是一个表达式,因此只能用作语句的一部分。
例如,您可以将函数赋给某个变量名称,尤其当您希望多次使用该函数时,这样做会更有意义。
|
Visual Basic
|
Dim add1 = Function(num As Integer) num + 1 |
若要调用函数,请为参数传递一个值。
|
Visual Basic
|
' The following line prints 6.
Console.WriteLine(add1(5))
|
或者,还可以同时声明和运行函数。
|
Visual Basic
|
Console.WriteLine((Function(num As Integer) num + 1)(5)) |
Lambda 表达式可以作为函数调用的值返回(如本主题后面“上下文”部分中的示例所示),或作为委托形参的实参进行传递。在下面的示例中,将布尔型 lambda 表达式作为方法 testResult 的参数进行传递。该方法向整数参数 value 应用布尔型测试,在将 lambda 表达式应用于 value 时,如果返回 True,则显示“Success”;如果返回 False,则显示“Failure”。
|
Visual Basic
|
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 ' Boolean function. ' 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 表达式 在语言集成查询 (LINQ) 中,lambda 表达式成为许多标准查询运算符的基础。编译器创建 lambda 表达式以捕获基础查询方法(例如 Where、Select、Order By、Take While 以及其他方法)中定义的计算。
例如,考虑下面的查询:
|
Visual Basic
|
Dim londonCusts = From cust In db.Customers Where cust.City = "London" Select cust |
此示例将编译为下面的代码:
|
Visual Basic
|
Dim londonCusts = db.Customers _ .Where(Function(cust) cust.City = "London") _ .Select(Function(cust) cust) |
有关查询方法的更多信息,请参见查询 (Visual Basic)。
Lambda 表达式语法 Lambda 表达式的语法类似于标准函数的语法。区别如下:
-
lambda 表达式没有名称。
-
Lambda 表达式不能有修饰符,例如 Overloads 或 Overrides。
-
Lambda 表达式不使用 As 子句来指定函数的返回类型。相反,类型是从 lambda 表达式主体计算得出的值推断而来的。例如,如果 lambda 表达式的主体为 Where cust.City = "London",则其返回类型为 Boolean。
-
函数体必须是表达式,不能是语句。函数体可以包含对函数过程的调用,但不能包含对子过程的调用。
-
不存在 Return 语句。函数返回的值是函数体中的表达式的值。
-
不存在 End Function 语句。
-
要么所有参数都必须具有指定的数据类型,要么必须推断所有类型。
-
不允许使用 Optional 和 Paramarray 参数。
-
不允许使用泛型参数。
由于存在上述限制,并且由于 lambda 表达式的使用方式,lambda 表达式通常简短而不复杂。
上下文 Lambda 表达式与在其中定义它的方法共享其上下文。它与在包含方法中编写的任何代码具有相同的访问权限。这包括访问包含方法中的成员变量、函数和子函数、Me 以及参数和局部变量的权限。
对包含方法中的局部变量和参数的访问可以超出该方法的生存期。只要引用 lambda 表达式的委托不能进行垃圾回收,就将保留对原始环境中的变量的访问。在下面的示例中,变量 target 对于 makeTheGame 方法(lambda 表达式 playTheGame 在该方法中定义)是局部变量。请注意,返回并分配给 Main 中 takeAGuess 的 lambda 表达式仍然具有访问局部变量 target 的权限。
|
Visual Basic
|
Module Module1 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
|
Visual Basic
|
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 表达式隐式转换为兼容的委托类型。有关兼容性的一般要求的更多信息,请参见宽松委托转换。
此外,在将 lambda 表达式分配给委托时,可以指定参数名称,但请省略其数据类型,以便从委托中获取类型。在下面的示例中,将 lambda 表达式分配给一个名为 del 的 ExampleDel 类型的变量以及一个接受两个参数(一个整数和一个字符串)的委托。请注意,没有指定 lambda 表达式中的参数的数据类型。但是,del 要求一个整数参数和一个字符串参数,如 ExampleDel 的定义中所指定的那样。
|
Visual Basic
|
' Definition of function delegate ExampleDel. Delegate Function ExampleDel(ByVal arg1 As Integer, _ ByVal arg2 As String) As Integer |
|
Visual Basic
|
' Declaration of del as an instance of ExampleDel, with no data ' type specified for the parameters, m and s. Dim del As ExampleDel = Function(m, s) m ' Valid call to del, sending in an integer and a string. Console.WriteLine(del(7, "up")) ' Neither of these calls is valid. Function del requires an integer ' argument and a string argument. ' Not valid. ' Console.WriteLine(del(7, 3)) ' Console.WriteLine(del("abc")) |
示例 -
下面的示例定义了一个 lambda 表达式,如果可以为 null 的参数已被赋值,则该表达式返回 True;如果该参数的值为 Nothing,则返回 False。
Visual BasicDim 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 表达式,该表达式返回数组中最后一个元素的索引。
Visual BasicDim numbers() As Integer = {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) = numbers(i) + 1 Next
说明:
浙公网安备 33010602011771号