MySQL Java JavaScript PHP Python HTML-CSS C-sharp

Lambda

Lambda-urile reprezintă o formă simplificată a metodelor anonime. Lambda-urile permit crearea unor metode concise, care pot returna o valoare și care pot fi transmise ca parametri în alte metode.

Lambda-urile au următorul sintaxă: în stânga operatorului lambda => se definește lista de parametri, iar în dreapta un bloc de expresii care utilizează acești parametri:

(lista_parametri) => expresie

Din punct de vedere al tipului de date, lambda-ul reprezintă un delegat. De exemplu, să definim un lambda simplu:

Message hello = () => Console.WriteLine("Hello");
hello();       // Hello
hello();       // Hello
hello();       // Hello

delegate void Message();

În acest caz, variabila hello reprezintă delegatul Message - adică o acțiune care nu returnează nimic și nu primește niciun parametru. Valoarea acestei variabile este un lambda.

Acest lambda trebuie să corespundă delegatului Message - nu primește niciun parametru, de aceea în stânga operatorului lambda sunt paranteze goale. În dreapta operatorului lambda se află expresia executată - Console.WriteLine("Hello").

Apoi, în program se poate apela această variabilă ca metodă.

Dacă lambda-ul conține mai multe acțiuni, acestea sunt plasate între acolade:

Message hello = () =>
{
   Console.Write("Hello ");
   Console.WriteLine("World");
};
hello();       // Hello World

Mai sus am definit variabila hello, care reprezintă delegatul Message. Dar începând cu versiunea C# 10, putem utiliza tipizarea implicită (definirea variabilei cu operatorul var) pentru lambda-uri:

var hello = () => Console.WriteLine("Hello");
hello();       // Hello
hello();       // Hello
hello();       // Hello

Dar ce tip reprezintă variabila hello în acest caz? La tipizarea implicită, compilatorul încearcă să potrivească lambda-ul cu un delegat. De exemplu, lambda-ul definit mai sus, hello, va fi tratat de compilator ca o variabilă de tipul delegatului încorporat Action, care nu primește niciun parametru și nu returnează nimic.

Parametrii lambda

La definirea listei de parametri, putem să nu specificăm tipul datelor pentru aceștia:

Operation sum = (x, y) => Console.WriteLine($"{x} + {y} = {x + y}");
sum(1, 2);       // 1 + 2 = 3
sum(22, 14);    // 22 + 14 = 36

delegate void Operation(int x, int y);

În acest caz, compilatorul vede că lambda-ul sum reprezintă tipul Operation, deci ambii parametri ai lambda-ului sunt de tipul int. Nu va fi nicio problemă.

Totuși, dacă folosim tipizarea implicită, compilatorul poate întâmpina dificultăți în deducerea tipului delegatului pentru lambda-ul respectiv, de exemplu:

var sum = (x, y) => Console.WriteLine($"{x} + {y} = {x + y}");   // ! Eroare

În acest caz, putem specifica tipul parametrilor:

var sum = (int x, int y) => Console.WriteLine($"{x} + {y} = {x + y}");
sum(1, 2);       // 1 + 2 = 3
sum(22, 14);    // 22 + 14 = 36

Dacă lambda-ul are un singur parametru pentru care nu trebuie specificat tipul datelor, parantezele pot fi omise:

PrintHandler print = message => Console.WriteLine(message);
print("Hello");         // Hello
print("Welcome");       // Welcome

delegate void PrintHandler(string message);

Începând cu C# 12, parametrii lambda-urilor pot avea valori implicite:

var welcome = (string message = "hello") => Console.WriteLine(message);

welcome("hello world"); // hello world
welcome();              // hello

Returnarea unui rezultat

Lambda-ul poate returna un rezultat. Rezultatul returnat poate fi specificat după operatorul lambda:

var sum = (int x, int y) => x + y;
int sumResult = sum(4, 5);                  // 9
Console.WriteLine(sumResult);               // 9

Operation multiply = (x, y) => x * y;
int multiplyResult = multiply(4, 5);        // 20
Console.WriteLine(multiplyResult);          // 20

delegate int Operation(int x, int y);

Dacă lambda-ul conține mai multe expresii (sau o singură expresie între acolade), trebuie folosit operatorul return, ca în metodele obișnuite:

var subtract = (int x, int y) =>
{
   if (x > y) return x - y;
   else return y - x;
};
int result1 = subtract(10, 6);  // 4
Console.WriteLine(result1);     // 4

int result2 = subtract(-10, 6);  // 16
Console.WriteLine(result2);      // 16

Adăugarea și eliminarea acțiunilor în lambda

Deoarece lambda-ul reprezintă un delegat, se pot adăuga metode și alte lambda-uri în variabila care reprezintă lambda-ul:

var hello = () => Console.WriteLine("FDC.COM");

var message = () => Console.Write("Hello ");
message += () => Console.WriteLine("World"); // adăugăm un lambda anonim
message += hello;   // adăugăm lambda-ul din variabila hello
message += Print;   // adăugăm metoda

message();
Console.WriteLine("--------------"); // pentru a separa ieșirea

message -= Print;   // eliminăm metoda
message -= hello;   // eliminăm lambda-ul din variabila hello

message?.Invoke();  // în cazul în care în message nu mai există acțiuni

void Print() => Console.WriteLine("Welcome to C#");
Hello World
FDC.COM
Welcome to C#
--------------
Hello World

Lambda-ul ca argument de metodă

La fel ca delegații, lambda-urile pot fi transmise ca parametri în metode care reprezintă un delegat:

int[] integers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

// găsim suma numerelor mai mari de 5
int result1 = Sum(integers, x => x > 5);
Console.WriteLine(result1); // 30

// găsim suma numerelor pare
int result2 = Sum(integers, x => x % 2 == 0);
Console.WriteLine(result2);  // 20

int Sum(int[] numbers, IsEqual func)
{
   int result = 0;
   foreach (int i in numbers)
   {
       if (func(i))
           result += i;
   }
   return result;
}

delegate bool IsEqual(int x);

Metoda Sum primește ca parametru un array de numere și un delegat IsEqual și returnează suma numerelor din array ca obiect int. În buclă, trecem prin toate numerele și le adunăm. Adunăm doar acele numere pentru care delegatul IsEqual func returnează true.

Adică delegatul IsEqual definește condiția pe care trebuie să o îndeplinească valorile array-ului. Dar la momentul scrierii metodei Sum, nu știm ce condiție este aceasta.

La apelul metodei Sum, îi transmitem array-ul și lambda-ul:

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

Parametrul x reprezintă numărul care este transmis delegatului:

if (func(i))

Expresia x > 5 reprezintă condiția pe care trebuie să o îndeplinească numărul. Dacă numărul îndeplinește această condiție, lambda-ul returnează true, iar numărul transmis se adaugă la celelalte numere.

În mod similar funcționează al doilea apel al metodei Sum, doar că aici se verifică dacă numărul este par, adică dacă restul împărțirii la 2 este zero:

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

Lambda-ul ca rezultat al metodei

Metoda poate de asemenea returna un lambda. În acest caz, tipul returnat al metodei este delegatul căruia îi corespunde lambda-ul returnat. De exemplu:

Operation operation = SelectOperation(OperationType.Add);
Console.WriteLine(operation(10, 4));    // 14

operation = SelectOperation(OperationType.Subtract);
Console.WriteLine(operation(10, 4));    // 6

operation = SelectOperation(OperationType.Multiply);
Console.WriteLine(operation(10, 4));    // 40

Operation SelectOperation(OperationType opType)
{
   switch (opType)
   {
       case OperationType.Add: return (x, y) => x + y;
       case OperationType.Subtract: return (x, y)

=> x - y;
       default: return (x, y) => x * y;
   }
}
enum OperationType
{
   Add, Subtract, Multiply
}
delegate int Operation(int x, int y);

În acest caz, metoda SelectOperation() primește ca parametru o enumerare de tipul OperationType. Această enumerare conține trei constante, fiecare corespunzând unei operații aritmetice specifice. În metoda însăși, în funcție de valoarea parametrului, returnăm un lambda corespunzător.

← Lecția anterioară Lecția următoare →