понедельник, 27 декабря 2021 г.

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

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

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

Ламбда-выражения имеют следующий синтаксис: слева от лямбда-оператора => определяется список параметров, а справа блок выражений, использующий эти параметры: (список_параметров) => выражение

Например:

1
2
3
4
5
6
7
8
9
10
11
class Program
{
    delegate int Operation(int x, int y);
    static void Main(string[] args)
    {
        Operation operation = (x, y) => x + y;
        Console.WriteLine(operation(10, 20));       // 30
        Console.WriteLine(operation(40, 20));       // 60
        Console.Read();
    }
}

Здесь код (x, y) => x + y; представляет лямбда-выражение, где x и y - это параметры, а x + y - выражение. 

При этом нам не надо указывать тип параметров, а при возвращении результата не надо использовать оператор return.

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

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Program 
{
    delegate bool IsEqual(int x);
     
    static void Main(string[] args)
    {
        int[] integers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
         
        // найдем сумму чисел больше 5
        int result1 = Sum(integers, x => x > 5);
        Console.WriteLine(result1); // 30
         
        // найдем сумму четных чисел
        int result2 = Sum(integers, x => x % 2 == 0);
        Console.WriteLine(result2);  //20
         
        Console.Read();
    }
 
    private static int Sum (int[] numbers, IsEqual func)
    {
        int result = 0;
        foreach(int i in numbers)
        {
            if (func(i))
                result += i;
        }
        return result;
    }
}

Метод Sum принимает в качестве параметра массив чисел и делегат IsEqual и возвращает сумму чисел массива в виде объекта int. В цикле проходим по всем числам и складываем их. Причем складываем только те числа, для которых делегат IsEqual func возвращает true. То есть делегат IsEqual здесь фактически задает условие, которому должны соответствовать значения массива. Но на момент написания метода Sum нам неизвестно, что это за условие.

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

1
int result1 = Sum(integers, x => x > 5);

То есть параметр x здесь будет представлять число, которое передается в делегат:

1
if (func(i))

А выражение x > 5 представляет условие, которому должно соответствовать число. Если число соответствует этому условию, то лямбда-выражение возвращает true, а переданное число складывается с другими числами.

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

1
int result2 = Sum(integers, x => x % 2 == 0);

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

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

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

    C #
    (input-parameters) => expression
    
  • Лямбда- выражение операторов, содержащее блок операторов в качестве тела:

    C #
    (input-parameters) => { <sequence-of-statements> }
    

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

В лямбда-выражениях лямбда-оператор =>отделяет входные параметры с левой стороны от тела лямбда с правой стороны.

Мой пример

 var filmOnMouse = _dbContext.Films

                .Include(f => f.ActorsOfFilm) 

// f – это Film (есть таблица Films. которая была описана созданным мной классом Film, в котором был описаны сущности таблицы Films ) 

//f.ActorsOfFilm - это связанная таблица с таблицей Films

                .ThenInclude(a => a.Person)

                .Include(f => f.GenresOfFilm)

                .ThenInclude(g => g.Genre)

                .Include(f => f.DirectorsOfFilm)

                .ThenInclude(d => d.Person)

                .Include(f => f.CountriesOfFilm)

                .ThenInclude(c => c.Country)

                .SingleOrDefault(f => f.Id == filmId);


            return filmOnMouse;


Для чего в C# используются лямбда-выражения?
Лямбда-выражения позволяют программировать функции в упрощенном виде без использования имени с помощью специального оператора, который обозначается ‘=>’.
3. Что такое лямбда-оператор?

Лямбда-оператор обозначается ‘=>’. Дословно можно сформировать лямбда-оператор как «переходит» или «становится». Оператор ‘=>’ делит лямбда-выражение на две части.
В левой части лямбда-оператора указывается один входной параметр или несколько входных параметров. В правой части лямбда-оператора указывается лямбда выражение.

4. Какие существуют виды лямбда-выражений?

В языке C# есть две разновидности лямбда-выражений:

  • одиночные лямбда-выражения;
  • блочные лямбда-выражения.
5. Какая общая форма объявления одиночного лямбда-выражения? Примеры

Общая форма объявления одиночного лямбда-выражения, которое принимает один параметр:

параметр => выражение

где

  • параметр – параметр, который получает на входе лямбда-выражение;
  • выражение – непосредственно выражение, которое вычисляется.

Общая форма объявления одиночного лямбда-выражения, которое принимает несколько параметров:

(список_параметров) => выражение

где

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

Пример 1 одиночного лямбда-выражения, которое вычисляет значение cos(x+2):

x => Math.Cos(x + 2);

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

double CalcCos2(double x)
{
    return Math.Cos(x + 2);
}

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

Пример 2 одиночного лямбда-выражения, которое получает 3 параметра с именами abc. В данном примере определяется, можно ли из длин сторон ab, c образовать треугольник?

(a, b, c) => ((a + b) > c) && ((a + c) > b) && ((b + c) > a);

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

7. Какие действия (шаги) нужно выполнить, чтобы в программе применить лямбда-выражение? Пример

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

  1. Объявить тип делегата, совместный с лямбда-выражением.
  2. Объявить переменную этого типа делегата (экземпляр делегата).
  3. Присвоить переменной (экземпляру делегата) лямбда-выражение.
  4. Вызвать переменную (экземпляр делегата) в программном коде.


Пример. Пройдем все шаги последовательно для задачи из предшествующего пункта (п.6). Пусть нужно продемонстрировать работу лямбда-выражения, которое получает входным параметром целое число x и вычисляет количество цифр ‘5’ в этом числе.

1. Объявить тип делегата. Тип делегата объявляется в некотором классе.

// Объявить тип делегата
delegate int CalcNum5(int x);

Имя типа делегата CalcNum5. Делегат этого типа будет получать один входной параметр (x). Делегат этого типа возвращает значение типа int.

2. Объявить переменную этого типа делегата. Переменная объявляется в некотором программном коде. Это может быть код произвольного метода класса, код обработчика события и т.п.

CalcNum5 CN;

3. Присвоить переменной лямбда-выражение. В методе объявления переменной объявляется присваивание:

CN = x =>
{
    int t, d, k;
    t = Math.Abs(x);
    k = 0; // количество цифр '5'
    while (t > 0)
    {
        d = t % 10;
        if (d == 5) k++;
        t = t / 10;
    }
    return k;
};

Переменная может быть инициализирована лямбда-выражением сразу при ее объявлении (п.2) в соответствии с синтаксисом языка C#. В сокращенном виде это выглядит так:

CalcNum5 CN = x =>
{
    ...
};

4. Вызвать переменную. В том же методе где объявлено лямбда-выражение реализован вызов переменной. Имя CN – это имя делегата, который содержит лямбда-выражение.

// использование лямбда-выражения
int a = 615;
int res;
res = CN(a); // res = 1

  

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

В данном примере продемонстрировано использование лямбда-выражения, в котором вычисляется функция y = sin²x.
Сначала объявляется тип делегата, который получает один параметр типа float и возвращает значение типа float. Параметр имеет название – x. В обработчике события button1_Click() продемонстрировано использование лямбда-выражения.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace TrainLyambda
{
    public partial class Form1 : Form
    {
        // объявить тип делегата, который получает 1 параметр и возвращает значение
        delegate float GetSin2X(float x);

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // Лямбда-выражение, которое получает 1 параметр и возвращает значение
            GetSin2X y = x => (float)(Math.Sin(x) * Math.Sin(x));
 
            // Демонстрация использования лямбда-выражения
            float z;
            const float Pi = 3.1415f;
            z = y(0.0f); // z = 0
            z = y((float)(Pi / 2.0)); // z = 1
            z = y(0.7f); // z = 0.4150164

            label1.Text = z.ToString();
        }
    }
}

  

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

В данном примере, для заданных xy вычисляется функция z = sin x² · cos y.

Чтобы объявить лямбда-выражение, сначала нужно объявить тип делегата, который получает 2 параметра и возвращает значение. Тип параметров и значения – double.

Объявление типа делегата имеет вид:

// объявить тип делегата, который получает 2 параметра и возвращает значение
delegate double CalcZ(double x, double y);

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

// Демонстрация использования лямбда-выражения
// Объявить переменную типа "делегат"
CalcZ Z;
 
// объявить лямбда-выражение, которое получает 2 параметра с именами x, y
Z = (x, y) => Math.Sin(x * x) - 2 * Math.Cos(y);

// использовать лямбда-выражение для расчета
double t;
t = Z(0.0, 0.0); // t = -2
t = Z(3.3, 1.8); // t = -0.540028

  

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

Задача

Дано три разных целых числа. Реализовать лямбда-выражение, которое находит наибольшее из этих трех чисел.

Решение

Сначала объявляется тип делегата, который получает три параметра целого типа и возвращает целое значение

// тип делегата, который получает 3 целочисленных параметра и возвращает целое значение
delegate int CalcMax(int a, int b, int c);

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

// Объявить делегат
CalcMax CM;

// описать лямбда-выражение, которое получает 3 параметра x, y, z
// и возвращает максимальное значение из этих параметров
CM = (x, y, z) =>
{
    int max = x;
    if (max < y) max = y;
    if (max < z) max = z;
    return max;
};

// использовать лямбда-выражение для поиска максимума
int a = 8, b = 5, c = 10;
int Max;

Max = CM(a, b, c); // Max = 10
Max = CM(a + 8, b - 3, c + 1); // Max = 16

  

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

Да, можно.

Пример.

Пусть объявлен тип делегата, получающий три параметра целого типа.

// объявление типа делегата
delegate int CalcABC(int a, int b, int c);

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

// Объявить делегат
CalcABC CM;

// 1. Описать лямбда-выражение, получающее 3 параметра x, y, z
// и возвращающее максимальное значение из этих параметров
CM = (x, y, z) =>
{
    int max = x;
    if (max < y) max = y;
    if (max < z) max = z;
    return max;
};

// использовать лямбда-выражение для поиска максимума
int a = 8, b = 5, c = 10;
int Max;

Max = CM(a, b, c); // Max = 10
Max = CM(a + 8, b - 3, c + 1); // Max = 16

label1.Text = "Максимум = " + Max.ToString();

// 2. Лямбда-выражение, вычисляющее сумму 3-х чисел
CM = (x, y, z) =>
{
    int s;
    s = z + x + y;
    return s;
};

int sum = CM(4, 3, 2); // sum = 9
label1.Text = "Сумма чисел = " + sum.ToString();

// 3. Лямбда-выражение, вычисляющее произведение из 3-х чисел
CM = (t, u, v) =>
{
    int mult = t * u * v;
    return mult;
};

int Mult = CM(5, 3, 10); // Mult = 150
label1.Text = "Произведение = " + Mult.ToString();

В вышеприведенном программном коде для одного типа делегата CalcABC реализовано 3 лямбда-выражения, которые выполняют следующие операции над входными параметрами:

  • находят максимальное значение;
  • находят сумму параметров;
  • находят произведение параметров.

Данный пример показывает преимущество применения делегатов в программах на C#. Делегат имеет одну сигнатуру (объявлена в типе), но в зависимости от ситуации при вызове этого делегата выполняется разная работа (программный код).

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

  

$

C #

public override string ToString()
{
   return $"{fname} {lname}".Trim();
}


Комментариев нет:

Отправить комментарий

Паттерн 'Репозиторий' в ASP.NET

  Последнее обновление: 1.11.2015         Одним из наиболее часто используемых паттернов при работе с данными является паттерн 'Репозито...