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
是主類型。 如需詳細資訊,請參閱 Widening and Narrowing Conversions。單行函式的主體必須是傳回值的運算式,而不是陳述式。 單行函式沒有
Return
陳述式。 單行函式所傳回的值,是函式主體中運算式的值。單行副程式主體必須是單行陳述式。
單行函式和副程式不包含
End Function
或End Sub
陳述式。您可以使用
As
關鍵字來指定 Lambda 運算式參數的資料類型,也可以推斷參數的資料類型。 所有參數都必須具有指定的資料類型,或必須推斷所有資料類型。不允許
Optional
和Paramarray
參數。不允許泛型參數。
非同步 Lambda
您可以使用 Async 和 Await 運算子關鍵字,輕鬆建立結合非同步處理的 Lambda 運算式和陳述式。 例如,下列 Windows Form 範例包含呼叫並等候非同步方法 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
您可以在 AddHandler 陳述式中使用非同步 Lambda 來新增相同的事件處理常式。 若要加入這個處理常式,請將 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 進行非同步程式設計。
上下文
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 運算式的各種存取權限。 當傳回的 Lambda 運算式以 aDel
的形式從 Main
執行時,它會存取下列元素:
定義類別的欄位:
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
' localVar = 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 運算式可以隱含地轉換成相容的委派型別。 如需相容性的一般需求相關資訊,請參閱寬鬆委派轉換。 例如,下列程式碼範例顯示隱含轉換成 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