CodeGym /Kursy /C# SELF /Skladnia wyrażeń lambda ( =&g...

Skladnia wyrażeń lambda ( =>)

C# SELF
Poziom 49, Lekcja 1
Dostępny

1. Wprowadzenie

Możesz o tym nie wiedzieć, ale jedno z ulubionych pytań na rozmowie kwalifikacyjnej to „Co wiesz o wyrażeniach lambda?”. Dlaczego o to pytają? Bo to bardzo wygodna i "modna" rzecz w C#, pozwalająca pisać mniej kodu i robić go bardziej wyrazistym. Jeśli metody anonimowe to jak pisać list ręcznie, to wyrażenia lambda to jak wysyłać wiadomość w komunikatorze: szybko, kompaktowo i czasem nawet z emotikonami (no, prawie).

Wyrażenia lambda pojawiły się w C# od wersji 3.0 i od tego czasu rozprzestrzeniły się wszędzie: w LINQ, handlerach zdarzeń, w wątkach, kolekcjach i nawet w bibliotekach AI na .NET.

Wyrażenie lambda — to sposób zadeklarowania metody bezpośrednio w treści kodu, "na miejscu", bez nazwy, bardzo krótko i czytelnie. Podstawowa składnia wygląda tak:

(parametry) => wyrażenie_lub_blok_kodu

Ten "dzióbek" => nazywa się „operator lambda” albo „strzałka”.

Mała analogia

Pomyśl o tym jak o pisaniu przepisu: „Weź jabłko, pokrój je, włóż do miski”.
W C# to byłoby:

(apple) => slice(apple)

2. Przerabiamy metodę anonimową na lmbdę

Załóżmy, że mamy delegata:

delegate int SquareDelegate(int x);

Metoda anonimowa wyglądałaby tak:

SquareDelegate sq = delegate(int x) {
    return x * x;
};

A teraz to samo przez wyrażenie lambda:

SquareDelegate sq = (int x) => { return x * x; };

Ale C# potrafi dopasować typy, więc możemy jeszcze skrócić:

SquareDelegate sq = x => x * x;

Imponująca kompaktowość!

Porównanie z "zwykłą" metodą i funkcją anonimową

Sposób Deklaracja Gdzie można użyć Wady
Metoda nazwana W klasie Wszędzie Wymaga nazwy, więcej kodu
Metoda anonimowa W kodzie Tylko z delegatami Bardziej rozbudowana składnia
Wyrażenie lambda W kodzie Wszędzie z delegatami Czasem nieoczywisty typ

3. Składnia wyrażenia lambda: warianty

Parametry

Bez parametrów:

Action hello = () => Console.WriteLine("Cześć, świat!");

Jeden parametr (można nie pisać nawiasów):

Func<int, int> inc = x => x + 1;

Kilka parametrów (trzeba nawiasy):

Func<int, int, int> sum = (a, b) => a + b;

Ciało lambdy

Jedno wyrażenie — bez klamr i return:

x => x * x

Blok kodu — klamry, potrzebny return:

(x, y) =>
{
    int z = x + y;
    return z * z;
}

Typy parametrów

Najczęściej nie trzeba określać typu — kompilator wywnioskuje. Ale jeśli chcesz, proszę bardzo:

(x, y) => x + y      // Kompilator sam wyprowadza typy.
(int x, int y) => x + y  // Można jawnie.

4. Niuanse i bardziej realne przykłady

Użycie z kolekcjami

Pamiętasz nasze ćwiczeniowe mini-aplikacje (np. lista użytkowników)? Załóżmy, że mamy tablicę liczb:

int[] numbers = { 1, 2, 3, 4, 5 };

Chcemy wybrać tylko liczby parzyste:

var evenNumbers = numbers.Where(n => n % 2 == 0);

Tu Where — metoda rozszerzająca LINQ, a warunek — nasz filtr lambda.

Przekazywanie do metody

Załóżmy, że mamy metodę-delegat:

public delegate bool Filter(int number);

public static int[] FilterNumbers(int[] data, Filter predicate)
{
    var result = new List<int>();
    foreach (var n in data)
        if (predicate(n))
            result.Add(n);
    return result.ToArray();
}

Teraz przekazujemy wyrażenie lambda:

int[] evens = FilterNumbers(numbers, n => n % 2 == 0);

Lambda jako handler zdarzeń

button.Click += (sender, args) => Console.WriteLine("Przycisk naciśnięty!");

5. Wyrażenia lambda i standardowe generyczne delegaty

Lambda trudno sobie wyobrazić bez Func<>, Action<> i Predicate<>. To specjalne typy delegatów:

  • Action — nic nie zwraca.
  • Func — zwraca wartość.
  • Predicate — zwraca bool, zwykle do filtrowania.

Przykład z Action:

Action<string> log = message => Console.WriteLine(message);

log("To wiadomość przez lambdę!");

Przykład z Func:

Func<int, int, int> multiply = (a, b) => a * b;
int product = multiply(3, 5); // 15

Przykład z Predicate:

Predicate<int> isNegative = n => n < 0;

bool test = isNegative(-7); // true

6. Lambdy z wieloma wyrażeniami: kiedy potrzebny blok

Jeśli lambda ma wykonać kilka operacji, użyj klamer i jawnego return:

Func<int, int, string> describeSum = (a, b) =>
{
    int sum = a + b;
    return $"Suma: {sum}";
};

Bez return kompilator się obrazi (ale nie wytłumaczy dokładnie dlaczego — po prostu napisze, że "nie wszystkie ścieżki zwracają wartość").

7. Zwracanie void: Action

Gdy lambda nic nie zwraca, użyj Action:

Action greet = () => Console.WriteLine("Uśmiechaj się — kod się kompiluje!");

Lambda może zawierać dowolną liczbę instrukcji:

Action<int> printSquare = x =>
{
    int sq = x * x;
    Console.WriteLine($"Kwadrat liczby {x} jest równy {sq}");
};

8. Ograniczenia i typowe błędy

Czasem chce się napisać lambdę bardzo swobodnie, ale kompilator nie zawsze się zgadza.

Nie można zadeklarować zmiennej o tej samej nazwie, co zmienna przechwycona z zewnątrz.

Trzeba pilnować typów: jeśli sygnatura delegata i lambdy się nie zgadzają — będzie błąd.

Zwrócenie wartości jest obowiązkowe, jeśli delegat wymaga wartości zwracanej.

W złożonych lambdach nie zapominaj o klamrach. Jedno wyrażenie — bez klamer i return. Kilka instrukcji — klamry i return.

2
Zadanie
C# SELF, poziom 49, lekcja 1
Niedostępne
Wyrażenie lambda z wieloma parametrami do dodawania liczb
Wyrażenie lambda z wieloma parametrami do dodawania liczb
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION