Что такое лямбда выражение
Перейти к содержимому

Что такое лямбда выражение

  • автор:

Объяснение лямбда-выражений

Объяснение лямбда-выражений

У меня возникли вопросы о лямбда-выражениях и RxJava. Эти вопросы в основном касаются не полного понимания лямбда-выражений или RxJava. Я попытаюсь объяснить лямбда-выражения как можно проще. RxJava я опишу отдельно.

Лямбда-выражения и RxJava

Что такое лямбда-выражения? Лямбда-выражения – это «всего лишь» новый способ сделать то же самое, что мы всегда могли сделать, но в более чистом и менее многословном новом способе использования анонимных внутренних классов.

Анонимный внутренний класс в Java – это класс без имени, он должен использоваться, если вам необходимо переопределить методы класса или интерфейса. Анонимный внутренний класс может быть создан из класса или интерфейса.

abstract class Animal < abstract void speak(); >Animal a = new Animal() < void speak() < System.out.println("Woff"); >>;

В Android мы обычно используем анонимный внутренний класс в качестве слушателя, например, для кнопок такого рода:

Button btn = findViewById(R.id.button); btn.setOnClickListener( new View.OnClickListener() < @Override public void onClick(final View view) < // Do some fancy stuff with the view parameter. >> );

Вернемся к лямбда-выражениям. Это следующая часть, которая является частью предыдущего кода, который считается анонимным внутренним классом.

new View.OnClickListener() < @Override public void onClick(final View view) < // Do some fancy stuff with the view parameter. >>

Лямбда-выражения могут использоваться только в том случае, если вам нужно переопределить не более одного метода. К счастью для нас, View.OnClickListener содержит только один. Посмотрите на код ниже. Как думаете, какую его часть нам придётся убрать?

new View.OnClickListener() < @Override public void onClick(final View view) < // Do some fancy stuff with the view parameter. >>

После удаления практически всего кода, нам нужно добавить ->, как в приведенном ниже коде. Входящий параметр view может использоваться внутри функции так же, как и раньше.

(view) -> < // Do some fancy stuff with the view parameter. >

В некоторых случаях вам может потребоваться добавить тип к параметру, если компилятору не удается его угадать, вы можете сделать это, добавив его перед параметром:

(View view) -> < // Do some fancy stuff with the view parameter. >

Вы также можете использовать многострочный код:

(view) ->

Если у вас есть интерфейс с методом, принимающим два параметра…

interface MyInterface

…лямбда-выражение будет выглядеть следующим образом:

Если метод имеет возвращаемый тип…

interface MySecondInterface

…лямбда-выражение будет выглядеть следующим образом:

Но это еще не все, есть некоторые специальные случаи, которые делают код еще меньше. Если тело вашего метода содержит только одну строку кода, вы можете удалить фигурные скобки <>. Если вы удаляете фигурные скобки, вам также необходимо удалить точку с запятой, оставив следующее:

(a, b) -> return a + b

Есть еще одна вещь, которую мы можем сделать. Если у нас только одна строка кода, то компилятор может понять, нужна ли возвращаемая часть или нет, поэтому мы можем оставить ее следующим образом:

(a, b) -> a + b

Если бы у нас был многострочный код, это свелось бы к следующему:

Так что же мы сделали? Мы взяли это:

new MySecondInterface() < @Override public int onSecondMethod(final int a, final int b) < return a + b; >>;

и превратили вот в это:

(a, b) -> a + b

Осталось только одна вещь, ссылки на методы. Допустим, у нас есть интерфейс, как и раньше, и метод, который этот интерфейс принимает как параметр:

public interface Callback < public void onEvent(int event); >public void myMethod(Callback callback)

Без лямбда-выражения это выглядело бы так:

myMethod(new Callback() < @Override public void onEvent(final int state) < System.out.println(state); >>); 

Добавив лямбда-выражение, как мы это делали раньше, получим следующее:

myMethod(state -> System.out.println(state));

Но это еще не все. Если используемый код – однострочный и вызываемая функция принимает один параметр, мы можем передавать ссылку на метод в таком виде:

myMethod(System.out::println);

Параметр будет передаваться автоматически, без необходимости использования другого кода! Удивительно, правда? Надеюсь, вы узнали что-то новое!

  • android development
  • android
  • java
  • rxJava
  • реактивное программирование
  • перевод с английского
  • программирование
  • разработка
  • devcolibri
  • андроид
  • джава
  • никто не читает теги
  • Программирование
  • Java
  • Разработка мобильных приложений
  • Разработка под Android

Лямбда-выражения (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 ' 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 = 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 = ' 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() = 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)
  • Практическое руководство. Создание лямбда-выражения
  • Неявное преобразование делегата

Совместная работа с нами на GitHub

Источник этого содержимого можно найти на GitHub, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.

Что такое лямбда выражение

Лямбда-выражения представляют более краткий компактный синтаксис для определения объектов-функций. Формальный синтаксис лямбда-выражения:

[] (параметры)

Лямбда-выражение начинается с квадратных скобок. Затем, как в обычной функции, в круглых скобках идет перечисление параметров — типы и их имена. Также начиная со стандарта C++14 для параметров можно указывать значения по умолчанию. За списком параметров, как и в обычной функции, в фигурных скобках помещаются действия, выполняемые лямбда-выражением.

Например, простейшее лямбда-выражение:

Здесь лямбда выражение не имеет параметров, поэтому указаны пустые скобки. Внутри лямбда-выражения просто выводится на консоль строка «Hello».

Каждый раз, когда компилятор встречает лямбда-выражение, он генерирует новый тип класса, который представляет объект-функцию. В примере выше сгенерированный класс упрощенно может выглядеть примерно так:

class __Lambda1234 < public: auto operator()() const < std::cout >;

Такой класс имеет произвольное, но уникальное сгенерированное имя. А действия лямбда-выражения определяются в виде оператора (), причем вместо возвращаемого типа применяется слово auto . То есть компилятор сам выводит возвращаемый тип, который может быть void , а может представлять какой-то определенный тип.

Для лямбда-функций без параметров вы можете опустить пустой список параметров (). То есть лямбда-выражение формы []() <. >может быть дополнительно сокращено до [] <. >:

Вызов лямбда-выражения

Мы можем непосредственно при определении сразу же вызвать лямбда-выражения, указав после тела выражения круглые скобки со значениями для параметров лямбды:

#include int main() < []()(); // или так [] (); >

Поскольку лямбда-выражения здесь не имеют параметров, то для вызова сразу после определения лямбды указываются пустые скобки

Это приведет к тому, что будут выполняться действия лямбда-выражения, и на консоль будет выведена строка «Hello».

Именнованные лямбда-выражения

Лямбда-выражение можно определить как переменную:

#include int main() < // переменная hello представляет лямбда-выражение auto hello < []()>; // через переменную вызываем лямбда-выражение hello(); // Hello hello(); // Hello >

Здесь переменная hello в качестве значения хранит лямбда-выражение. Чтобы компилятор автоматически определил тип переменной, она определена с ключевым словом auto

Далее через имя переменной мы можем вызвать лямбда-выражение как обычную функцию:

hello();

Параметры

Теперь определим лямбда-выражение с двумя параметрами:

#include int main() < auto print < [](const std::string& text)>; // вызываем лямбда-выражение print("Hello World!"); // Hello World! print("Good bye, World. "); // Good bye, World. >

Здесь лямбда-выражение принимает один параметр типа const std::string& , то есть строку, которая выводится на консоль. И это лямбда-выражение присвоено переменной print.

Вызывая print как стандартную функцию, нужно передать в нее некоторую строку:

print("Hello World!"); // Hello World! print("Good bye, World. "); // Good bye, World.

Также можно сразу же при определении вызвать лямбда-выражение, передав в него строку

[](const std::string& text) ("Hell");

Возвращение значения

Лямбда-выражение может возвращать произвольное значение. В этом случае, как и в обычной функции, применяется оператор return :

#include int main() < auto sum < [](int a, int b)>; // вызываем лямбда-выражение std::cout ; std::cout 

В данном случае лямбда-выражение возвращает сумму параметров в виде значения int. Соответственно результат выражения мы можем использовать как значение типа int.

Стоит отметить, что по умолчанию компилятор сам определяет, значение какого именно типа будет возвращаться из лямбды. Однако мы также можем явным образом указать возвращаемый тип:

#include int main() < auto sum < [](int a, int b) ->double >; // вызываем лямбда-выражение std::cout 

Для установки возвращаемого типа после списка параметров указывается стрелка и собственно возвращаемый тип. Так, в данном случае возвращается значение типа double:

[](int a, int b) -> double

То есть сумма чисел, которая по умолчанию представляет тип int, будет преобразована в значение типа double.

Лямбда-выражения как параметры функций

Лямбда-выражение может передаваться в качестве значения параметру функции, который представляет указатель на функцию:

#include void do_operation(int a, int b, int (*op)(int, int)) < std::cout int main() < auto sum < [](int a, int b) >; auto subtract < [](int a, int b) >; do_operation(10, 4, sum); // 14 do_operation(10, 4, subtract); // 6 >

Здесь определена функция do_operation, которая принимает два числа и указатель на функцию — операцию над этими числами. На место указателя на функцию мы можем передать лямбда-выражение, которое соответствует этому указателю. Это возможно благодаря тому, что компилятор может автоматически сгенерировать для лямбда-выражения оператор преобразования типа в эквивалентный тип указателя функции. Следует отметить, что оператор преобразования не генерируется, если лямбда-выражения обращается к внешним переменным, и далее мы рассмотрим подобную ситуацию.

Также можно определить лямбда-выражение непосредственно при использовании, что может быть полезно, если лямбду больше нигде не планируется использовать:

do_operation(10, 4, [](int a, int b) ); // 40

Универсальные лямбда-выражения

Универсальное лямбда-выражение (generic lambda) — это лямбда-выражение, в котором как минимум для одного параметра в качестве типа указано слово auto или выражения auto& или const auto& . Это позволяет уйти от жесткой привязки параметров к определенному типу. Например:

#include int main() < auto add = [](auto a, auto b) ; //auto print = [](const auto& value) ; std::cout ; std::string world; std::cout 

В данном случае определено лямбда-выражение, которое принимает два параметра и возвращает их сумму. Оно присвоено переменной add:

auto add = [](auto a, auto b) ;

То есть на момент написания мы не знаем, какие типы будут представлять параметры. Конкретные типы будет выводить компилятор при вызове лямбда-выражения исходя из переданных в него значений:

std::cout 

Так, в данном случае передаются два числа типа int, соответственно результат будет сумма этих чисел в виде значения int.

Другой пример - определим универсальное лямбда-выражение, которое выводит произвольное значение на консоль:

#include int main() < auto print = [](const auto& value) ; print("Hello"); print(4); print(45.6789); >

Лямбда-выражения и анонимные функции

Лямбда-выражение используется для создания анонимной функции. Используйте оператор объявления лямбда-выражения => для отделения списка параметров лямбда-выражения от исполняемого кода. Лямбда-выражение может иметь одну из двух следующих форм:

    Лямбда выражения, имеющая выражение в качестве текста:

(input-parameters) => expression 
(input-parameters) => < > 

Чтобы создать лямбда-выражение, необходимо указать входные параметры (если они есть) с левой стороны лямбда-оператора и блок выражений или операторов с другой стороны.

Лямбда-выражение может быть преобразовано в тип делегата. Тип делегата, в который может быть преобразовано лямбда-выражение, определяется типами его параметров и возвращаемым значением. Если лямбда-выражение не возвращает значение, оно может быть преобразовано в один из типов делегата Action ; в противном случае его можно преобразовать в один из типов делегатов Func . Например, лямбда-выражение, которое имеет два параметра и не возвращает значение, можно преобразовать в делегат Action . Лямбда-выражение, которое имеет два параметра и возвращает значение, можно преобразовать в делегат Func . В следующем примере лямбда-выражение x => x * x , которое указывает параметр с именем x и возвращает значение x в квадрате, присваивается переменной типа делегата:

Func square = x => x * x; Console.WriteLine(square(5)); // Output: // 25 

Лямбда-выражения можно также преобразовать в типы дерева выражения, как показано в следующем примере:

System.Linq.Expressions.Expression> e = x => x * x; Console.WriteLine(e); // Output: // x => (x * x) 

Лямбда-выражения можно использовать в любом коде, для которого требуются экземпляры типов делегатов или деревьев выражений, например в качестве аргумента метода Task.Run(Action) для передачи кода, который должен выполняться в фоновом режиме. Можно также использовать лямбда-выражения при применении LINQ в C#, как показано в следующем примере:

int[] numbers = < 2, 3, 4, 5 >; var squaredNumbers = numbers.Select(x => x * x); Console.WriteLine(string.Join(" ", squaredNumbers)); // Output: // 4 9 16 25 

При использовании синтаксиса на основе методов для вызова метода Enumerable.Select в классе System.Linq.Enumerable (например, в LINQ to Objects и LINQ to XML) параметром является тип делегата System.Func . При вызове метода Queryable.Select в классе System.Linq.Queryable (например, в LINQ to SQL) типом параметра является тип дерева выражения Expression> . В обоих случаях можно использовать одно и то же лямбда-выражение для указания значения параметра. Поэтому оба вызова Select выглядят одинаково, хотя на самом деле объект, созданный из лямбда-выражения, имеет другой тип.

Выражения-лямбды

Лямбда-выражение с выражением с правой стороны оператора => называется выражением лямбда. Выражения-лямбды возвращают результат выражения и принимают следующую основную форму.

(input-parameters) => expression 

Текст выражения лямбды может состоять из вызова метода. Но при создании деревьев выражений, которые вычисляются вне контекста поддержки общеязыковой среды выполнения (CRL) .NET, например в SQL Server, вызовы методов не следует использовать в лямбда-выражениях. Методы не имеют смысла вне контекста среды CLR .NET.

Лямбды операторов

Лямбда-инструкция напоминает лямбда-выражение, за исключением того, что инструкции заключаются в фигурные скобки:

(input-parameters) => < > 

Тело лямбды оператора может состоять из любого количества операторов; однако на практике обычно используется не более двух-трех.

Action greet = name => < string greeting = $"Hello !"; Console.WriteLine(greeting); >; greet("World"); // Output: // Hello World! 

Лямбда-инструкции нельзя использовать для создания деревьев выражений.

Входные параметры лямбда-выражения

Входные параметры лямбда-выражения заключаются в круглые скобки. Нулевое количество входных параметров задается пустыми скобками:

Action line = () => Console.WriteLine(); 

Если лямбда-выражение имеет только один входной параметр, круглые скобки необязательны:

Func cube = x => x * x * x; 

Два и более входных параметра разделяются запятыми:

Func testForEquality = (x, y) => x == y; 

Иногда компилятор не может вывести типы входных параметров. Вы можете указать типы данных в явном виде, как показано в следующем примере:

Func isTooLong = (int x, string s) => s.Length > x; 

Для входных параметров все типы нужно задать либо в явном, либо в неявном виде. В противном случае компилятор выдает ошибку CS0748.

Вы можете использовать dis карта s для указания двух или нескольких входных параметров лямбда-выражения, которые не используются в выражении:

Func constant = (_, _) => 42; 

Параметры пустой переменной лямбда-выражения полезны, если вы используете лямбда-выражение для указания обработчика событий.

Если только один входной параметр имеет имя _ , для обеспечения обратной совместимости _ рассматривается как имя этого параметра в лямбда-выражении.

Начиная с C# 12, можно указать значения по умолчанию для параметров в лямбда-выражениях. Синтаксис и ограничения значений параметров по умолчанию совпадают с методами и локальными функциями. В следующем примере объявляется лямбда-выражение с параметром по умолчанию, а затем вызывает его один раз с использованием значения по умолчанию и один раз с двумя явными параметрами:

var IncrementBy = (int source, int increment = 1) => source + increment; Console.WriteLine(IncrementBy(5)); // 6 Console.WriteLine(IncrementBy(5, 2)); // 7 

Можно также объявить лямбда-выражения с массивами в params качестве параметров:

var sum = (params int[] values) => < int sum = 0; foreach (var value in values) sum += value; return sum; >; var empty = sum(); Console.WriteLine(empty); // 0 var sequence = new[] < 1, 2, 3, 4, 5 >; var total = sum(sequence); Console.WriteLine(total); // 15 

В рамках этих обновлений, когда группе методов, которая имеет параметр по умолчанию, назначается лямбда-выражение, это лямбда-выражение также имеет тот же параметр по умолчанию. Группу методов с параметром массива params также можно назначить лямбда-выражению.

Лямбда-выражения с параметрами или params массивами по умолчанию в качестве параметров не имеют естественных типов, соответствующих Func<> или Action<> типам. Однако можно определить типы делегатов, которые включают значения параметров по умолчанию:

delegate int IncrementByDelegate(int source, int increment = 1); delegate int SumDelegate(params int[] values); 

Кроме того, можно использовать неявно типизированные переменные с var объявлениями для определения типа делегата. Компилятор синтезирует правильный тип делегата.

Дополнительные сведения см. в спецификации компонентов для параметров по умолчанию для лямбда-выражений.

Асинхронные лямбда-выражения

С помощью ключевых слов async и await можно легко создавать лямбда-выражения и операторы, включающие асинхронную обработку. Например, в следующем примере Windows Forms содержится обработчик событий, который вызывает асинхронный метод ExampleMethodAsync и ожидает его.

public partial class Form1 : Form < public Form1() < InitializeComponent(); button1.Click += button1_Click; >private async void button1_Click(object sender, EventArgs e) < await ExampleMethodAsync(); textBox1.Text += "\r\nControl returned to Click event handler.\n"; >private async Task ExampleMethodAsync() < // The following line simulates a task-returning asynchronous process. await Task.Delay(1000); >> 

Такой же обработчик событий можно добавить с помощью асинхронного лямбда-выражения. Чтобы добавить этот обработчик, поставьте модификатор async перед списком параметров лямбда-выражения, как показано в следующем примере:

public partial class Form1 : Form < public Form1() < InitializeComponent(); button1.Click += async (sender, e) =>< await ExampleMethodAsync(); textBox1.Text += "\r\nControl returned to Click event handler.\n"; >; > private async Task ExampleMethodAsync() < // The following line simulates a task-returning asynchronous process. await Task.Delay(1000); >> 

Дополнительные сведения о создании и использовании асинхронных методов см. в разделе Асинхронное программирование с использованием ключевых слов Async и Await.

Лямбда-выражения и кортежи

Язык C# обеспечивает встроенную поддержку кортежей. Кортеж можно ввести в качестве аргумента лямбда-выражения, и лямбда-выражение также может возвращать кортеж. В некоторых случаях компилятор C# использует определение типа для определения типов компонентов кортежа.

Кортеж определяется путем заключения в скобки списка его компонентов с разделителями-запятыми. В следующем примере кортеж с тремя компонентами используется для передачи последовательности чисел в лямбда-выражение. Оно удваивает каждое значение и возвращает кортеж с тремя компонентами, содержащий результат операций умножения.

Func <(int, int, int), (int, int, int)>doubleThem = ns => (2 * ns.Item1, 2 * ns.Item2, 2 * ns.Item3); var numbers = (2, 3, 4); var doubledNumbers = doubleThem(numbers); Console.WriteLine($"The set doubled: "); // Output: // The set (2, 3, 4) doubled: (4, 6, 8) 

Как правило, поля кортежи именуются как Item1 , Item2 и т. д. Тем не менее кортеж с именованными компонентами можно определить, как показано в следующем примере:

Func <(int n1, int n2, int n3), (int, int, int)>doubleThem = ns => (2 * ns.n1, 2 * ns.n2, 2 * ns.n3); var numbers = (2, 3, 4); var doubledNumbers = doubleThem(numbers); Console.WriteLine($"The set doubled: "); 

Дополнительные сведения о кортежах в C# см. в статье Типы кортежей.

Лямбда-выражения со стандартными операторами запросов

public delegate TResult Func(T arg) 

Экземпляр этого делегата можно создать как Func , где int — входной параметр, а bool — возвращаемое значение. Возвращаемое значение всегда указывается в последнем параметре типа. Например, Func определяет делегат с двумя входными параметрами, int и string , и типом возвращаемого значения bool . Следующий делегат Func при вызове возвращает логическое значение, которое показывает, равен ли входной параметр 5:

Func equalsFive = x => x == 5; bool result = equalsFive(4); Console.WriteLine(result); // False 

В этом примере используется стандартный оператор запроса Count:

int[] numbers = < 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 >; int oddNumbers = numbers.Count(n => n % 2 == 1); Console.WriteLine($"There are odd numbers in "); 

Компилятор может вывести тип входного параметра ввода; но его также можно определить явным образом. Данное лямбда-выражение подсчитывает указанные целые значения ( n ), которые при делении на два дают остаток 1.

В следующем примере кода показано, как создать последовательность, которая содержит все элементы массива numbers , предшествующие 9, так как это первое число последовательности, не удовлетворяющее условию:

int[] numbers = < 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 >; var firstNumbersLessThanSix = numbers.TakeWhile(n => n < 6); Console.WriteLine(string.Join(" ", firstNumbersLessThanSix)); // Output: // 5 4 1 3 

В следующем примере показано, как указать несколько входных параметров путем их заключения в скобки. Этот метод возвращает все элементы в массиве numbers до того числа, значение которого меньше его порядкового номера в массиве:

int[] numbers = < 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 >; var firstSmallNumbers = numbers.TakeWhile((n, index) => n >= index); Console.WriteLine(string.Join(" ", firstSmallNumbers)); // Output: // 5 4 

Лямбда-выражения не используются непосредственно в выражениях запросов, но их можно использовать в вызовах методов в выражениях запросов, как показано в следующем примере:

var numberSets = new List < new[] < 1, 2, 3, 4, 5 >, new[] < 0, 0, 0 >, new[] < 9, 8 >, new[] < 1, 0, 1, 0, 1, 0, 1, 0 >>; var setsWithManyPositives = from numberSet in numberSets where numberSet.Count(n => n > 0) > 3 select numberSet; foreach (var numberSet in setsWithManyPositives) < Console.WriteLine(string.Join(" ", numberSet)); >// Output: // 1 2 3 4 5 // 1 0 1 0 1 0 1 0 

Определение типа в лямбда-выражениях

При написании лямбда-выражений обычно не требуется указывать тип входных параметров, так как компилятор может выводить этот тип на основе тела лямбда-выражения, типов параметров и других факторов, как описано в спецификации языка C#. Для большинства стандартных операторов запросов первой входное значение имеет тип элементов в исходной последовательности. При запросе IEnumerable входная переменная считается объектом Customer , а это означает, что у вас есть доступ к его методам и свойствам:

customers.Where(c => c.City == "London"); 

Общие правила определения типа для лямбда-выражений формулируются следующим образом:

  • лямбда-выражение должно содержать то же число параметров, что и тип делегата;
  • каждый входной параметр в лямбда-выражении должен быть неявно преобразуемым в соответствующий параметр делегата;
  • возвращаемое значение лямбда-выражения (если таковое имеется) должно быть неявно преобразуемым в возвращаемый тип делегата.

Естественный тип лямбда-выражения

Лямбда-выражение само по себе не имеет типа, так как система общих типов не имеет встроенной концепции "лямбда-выражения". Однако иногда удобно говорить о "типе" лямбда-выражения. Под неофициальным термином "тип" понимается тип делегата или тип Expression, в который преобразуется лямбда-выражение.

Начиная с C# 10, лямбда-выражение может иметь естественный тип. Вам не потребуется объявлять тип делегата, например Func <. >или Action <. >для лямбда-выражения, потому что компилятор может вывести тип делегата из лямбда-выражения. В качестве примера рассмотрим следующее объявление:

var parse = (string s) => int.Parse(s); 

Компилятор может определить parse как Func . Компилятор использует доступный делегат Func или Action , если он существует. Если нет, компилятор синтезирует тип делегата. Например, тип делегата синтезируется, если лямбда-выражение имеет параметры ref . Если лямбда-выражение имеет естественный тип, его можно присвоить менее явному типу, например System.Object или System.Delegate:

object parse = (string s) => int.Parse(s); // Func Delegate parse = (string s) => int.Parse(s); // Func

Группы методов (то есть имена методов без списков параметров) с ровно одной перегрузкой имеют естественный тип:

var read = Console.Read; // Just one overload; Func inferred var write = Console.Write; // ERROR: Multiple overloads, can't choose 
LambdaExpression parseExpr = (string s) => int.Parse(s); // Expression> Expression parseExpr = (string s) => int.Parse(s); // Expression> 

Не у всех лямбда-выражений есть естественный тип. Рассмотрим следующее объявление:

var parse = s => int.Parse(s); // ERROR: Not enough type info in the lambda 

Компилятор не может определить тип параметра для s . Если компилятор не может определить естественный тип, необходимо объявить тип:

Func parse = s => int.Parse(s); 

Явный тип возвращаемого значения

Как правило, тип возвращаемого значения лямбда-выражения является очевидным и легко выводится. Для некоторых выражений, которые не работают:

var choose = (bool b) => b ? 1 : "two"; // ERROR: Can't infer return type 

Начиная с C# 10, можно указать тип возвращаемого значения лямбда-выражения перед входными параметрами. Если вы указываете явный тип возвращаемого значения, заключите входные параметры в скобки:

var choose = object (bool b) => b ? 1 : "two"; // Func

Атрибуты

Начиная с C# 10, вы можете добавлять атрибуты в лямбда-выражение и его параметры. В следующем примере показано, как добавить атрибуты в лямбда-выражение:

Func parse = [ProvidesNullCheck] (s) => (s is not null) ? int.Parse(s) : null; 

Кроме того, вы можете добавить атрибуты во входные параметры или возвращаемое значение, как показано в следующем примере:

var concat = ([DisallowNull] string a, [DisallowNull] string b) => a + b; var inc = [return: NotNullIfNotNull(nameof(s))] (int? s) => s.HasValue ? s++ : null; 

Как показано в предыдущих примерах, при добавлении атрибутов в лямбда-выражение или его параметры вам нужно заключить входные параметры в скобки.

Лямбда-выражения вызываются через базовый тип делегата. Это отличается от методов и локальных функций. Метод делегата Invoke не проверяет атрибуты в лямбда-выражении. При вызове лямбда-выражения атрибуты не оказывают никакого влияния. Атрибуты лямбда-выражений полезны для анализа кода и могут быть обнаружены с помощью отражения. Одно из последствий этого решения — невозможность применить System.Diagnostics.ConditionalAttribute к лямбда-выражению.

Запись внешних переменных и области видимости переменной в лямбда-выражениях

Лямбда-выражения могут ссылаться на внешние переменные. Эти внешние переменные являются переменными, которые находятся в область в методе, определяющем лямбда-выражение, или в область в типе, который содержит лямбда-выражение. Переменные, полученные таким способом, сохраняются для использования в лямбда-выражениях, даже если бы в ином случае они оказались за границами области действия и уничтожились сборщиком мусора. Внешняя переменная должна быть определенным образом присвоена, прежде чем она сможет использоваться в лямбда-выражениях. В следующем примере демонстрируются эти правила.

public static class VariableScopeWithLambdas < public class VariableCaptureGame < internal Action? updateCapturedLocalVariable; internal Func? isEqualToCapturedLocalVariable; public void Run(int input) < int j = 0; updateCapturedLocalVariable = x => < j = x; bool result = j >input; Console.WriteLine($" is greater than : "); >; isEqualToCapturedLocalVariable = x => x == j; Console.WriteLine($"Local variable before lambda invocation: "); updateCapturedLocalVariable(10); Console.WriteLine($"Local variable after lambda invocation: "); > > public static void Main() < var game = new VariableCaptureGame(); int gameInput = 5; game.Run(gameInput); int jTry = 10; bool result = game.isEqualToCapturedLocalVariable!(jTry); Console.WriteLine($"Captured local variable is equal to : "); int anotherJ = 3; game.updateCapturedLocalVariable!(anotherJ); bool equalToAnother = game.isEqualToCapturedLocalVariable(anotherJ); Console.WriteLine($"Another lambda observes a new value of captured variable: "); > // Output: // Local variable before lambda invocation: 0 // 10 is greater than 5: True // Local variable after lambda invocation: 10 // Captured local variable is equal to 10: True // 3 is greater than 5: False // Another lambda observes a new value of captured variable: True > 

Следующие правила применимы к области действия переменной в лямбда-выражениях.

  • Захваченная переменная не будет уничтожена сборщиком мусора до тех пор, пока делегат, который на нее ссылается, не перейдет в статус подлежащего уничтожению при сборке мусора.
  • Переменные, представленные в лямбда-выражении, невидимы в заключающем методе.
  • Лямбда-выражение не может непосредственно захватывать параметры in, ref или out из заключающего метода.
  • Оператор return в лямбда-выражении не вызывает возврат значения заключающим методом.
  • Лямбда-выражение не может содержать операторы goto, break или continue, если целевой объект этого оператора перехода находится за пределами блока лямбда-выражения. Если целевой объект находится внутри блока, использование оператора перехода за пределами лямбда-выражения также будет ошибкой.

Модификатор можно применить static к лямбда-выражению, чтобы предотвратить непреднамеренный захват локальных переменных или состояния экземпляра лямбда-выражения:

Func square = static x => x * x; 

Статическое лямбда-выражение не может сохранять локальные переменные или состояние экземпляров из охватывающих областей, но может ссылаться на статические элементы и определения констант.

Спецификация языка C#

Дополнительные сведения об этих функциях см. в следующих заметках о предложении функций:

  • Параметры лямбда-дис карта
  • Статические анонимные функции
  • Улучшения лямбда-выражений (C# 10)

См. также

  • Используйте локальную функцию вместо лямбда-правила (правило стиля IDE0039)
  • Операторы и выражения C#
  • Встроенный язык запросов LINQ
  • Деревья выражений
  • Локальные функции или лямбда-выражения
  • Примеры запросов LINQ
  • Пример XQuery
  • Общие сведения о примерах LINQ

Совместная работа с нами на GitHub

Источник этого содержимого можно найти на GitHub, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *