Лямбда-выражения (Visual Basic)
Лямбда-выражение — это функция или подзадачи без имени, которое можно использовать везде, где делегат действителен. Лямбда-выражения могут быть функциями или вложенными значениями и могут быть однострочные или многострочные. Можно передать значения из текущего область в лямбда-выражение.
Примечание.
Оператор RemoveHandler
является исключением. Нельзя передать лямбда-выражение для параметра делегата RemoveHandler
.
Вы создаете лямбда-выражения с помощью Function
или Sub
ключевое слово так же, как вы создаете стандартную функцию или подпрограмму. Однако лямбда-выражения включаются в инструкцию.
Следующий пример — лямбда-выражение, которое увеличивает его аргумент и возвращает значение. В примере показан синтаксис однострочного и многострочного лямбда-выражения для функции.
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))
В следующем примере используется лямбда-выражение, которое записывает значение в консоль. В примере показан синтаксис однострочного и многострочного лямбда-выражения для подпрограммы.
Dim writeline1 = Sub(x) Console.WriteLine(x)
Dim writeline2 = Sub(x)
Console.WriteLine(x)
End Sub
' Write "Hello".
writeline1("Hello")
' Write "World"
writeline2("World")
Обратите внимание, что в предыдущих примерах лямбда-выражения назначаются имени переменной. Всякий раз, когда вы ссылаетесь на переменную, вызывается лямбда-выражение. Можно также объявить и вызвать лямбда-выражение одновременно, как показано в следующем примере.
Console.WriteLine((Function(num As Integer) num + 1)(5))
Лямбда-выражение может быть возвращено как значение вызова функции (как показано в примере в разделе контекста далее в этом разделе) или передано в качестве аргумента параметру, который принимает тип делегата, как показано в следующем примере.
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
Синтаксис лямбда-выражений
Синтаксис лямбда-выражения похож на стандартную функцию или подпрограмму. Имеются следующие различия:
Лямбда-выражение не имеет имени.
Лямбда-выражения не могут иметь модификаторы, например
Overloads
илиOverrides
.Однострочные лямбда-функции не используют
As
предложение для обозначения возвращаемого типа. Вместо этого тип выводится из значения, которое вычисляется текстом лямбда-выражения. Например, если текст лямбда-выражения имеет значениеcust.City = "London"
, возвращаетсяBoolean
тип.В лямбда-функциях с несколькими строками можно указать тип возвращаемого значения с помощью
As
предложения или опуститьAs
предложение, чтобы возвращаемый тип был выведен.As
Если предложение опущено для лямбда-функции с несколькими строками, возвращаемый тип определяется как доминирующий тип из всехReturn
инструкций в лямбда-функции с несколькими строками. Доминирующий тип — это уникальный тип , к которому могут расшириться все остальные типы. Если этот уникальный тип не может быть определен, доминирующий тип является уникальным типом, к которому могут быть сузить все остальные типы массива. Если ни один из указанных уникальных типов нельзя определить, главным типом будетObject
. В этом случае, еслиOption Strict
задано значениеOn
, возникает ошибка компилятора.Например, если выражения, предоставленные
Return
инструкции, содержат значения типаInteger
,Long
иDouble
результирующий массив имеет типDouble
. ОбаInteger
иLong
только расширяется доDouble
и толькоDouble
. ПоэтомуDouble
является главным типом. Для получения дополнительной информации см. Widening and Narrowing Conversions.Текст однострочного функции должен быть выражением, возвращающим значение, а не оператором.
Return
Нет инструкции для однострочных функций. Значение, возвращаемое одной строкой функции, является значением выражения в тексте функции.Текст однострочного подзадачи должен быть однострочного оператора.
Однострочные функции и вложенные функции не включают инструкцию или
End Sub
инструкциюEnd Function
.Можно указать тип данных параметра лямбда-выражения с помощью
As
ключевое слово или типа данных параметра. Все параметры должны иметь указанные типы данных или все должны быть выведены.Optional
иParamarray
параметры не разрешены.Универсальные параметры не разрешены.
Асинхронные лямбда-выражения
Вы можете легко создавать лямбда-выражения и инструкции, которые включают асинхронную обработку с помощью ключевое слово операторов Async и Await. Например, в следующем примере Windows Forms содержится обработчик событий, который вызывает асинхронный метод 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. Чтобы добавить этот обработчик, поставьте модификатор Async
перед списком параметров лямбда-выражения, как показано в следующем примере.
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
Дополнительные сведения о создании и использовании асинхронных методов см. в статье "Асинхронное программирование" и "Ожидание".
Контекст
Лямбда-выражение разделяет контекст с область, в которой она определена. Он имеет те же права доступа, что и любой код, написанный в содержащие область. Сюда входит доступ к переменным-членам, функциям и дочернимMe
, а также параметрам и локальным переменным в содержащихся область.
Доступ к локальным переменным и параметрам в содержащей область может превышать время существования этого область. Пока делегат, ссылающийся на лямбда-выражение, недоступен для сборки мусора, сохраняется доступ к переменным в исходной среде. В следующем примере переменная target
является локальной makeTheGame
для метода, в котором определяется лямбда-выражение playTheGame
. Обратите внимание, что возвращаемое лямбда-выражение, назначенное takeAGuess
в Main
, по-прежнему имеет доступ к локальной переменной 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
В следующем примере демонстрируется широкий спектр прав доступа вложенного лямбда-выражения. При выполнении возвращаемого лямбда-выражения из Main
as aDel
он обращается к этим элементам:
Поле класса, в котором он определен:
aField
Свойство класса, в котором он определен:
aProp
Параметр метода
functionWithNestedLambda
, в котором он определен:level1
Локальная переменная
functionWithNestedLambda
:localVar
Параметр лямбда-выражения, в котором он вложен:
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
Преобразование в тип делегата
Лямбда-выражение может быть неявно преобразовано в совместимый тип делегата. Сведения о общих требованиях к совместимости см. в разделе "Расслабленное преобразование делегатов". Например, в следующем примере кода показана лямбда-выражение, которое неявно преобразуется в Func(Of Integer, Boolean)
или соответствующую подпись делегата.
' 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)
или соответствующую подпись делегата.
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
При назначении лямбда-выражений делегатам или передаче их в качестве аргументов в процедуры можно указать имена параметров, но опустить их типы данных, позволяя принимать типы из делегата.
Примеры
В следующем примере определяется лямбда-выражение, которое возвращает
True
, если аргумент типа значения NULL имеет назначенное значение, аFalse
если его значение равноNothing
.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))
В следующем примере определяется лямбда-выражение, которое возвращает индекс последнего элемента в массиве.
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
См. также
- Процедуры
- Знакомство с LINQ в Visual Basic
- Делегаты
- Оператор Function
- Оператор Sub
- Типы значений, допускающие значение NULL
- How to: Pass Procedures to Another Procedure in Visual Basic (Практическое руководство. Передача процедур другой процедуре в Visual Basic)
- Практическое руководство. Создание лямбда-выражения
- Неявное преобразование делегата