Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Wskazówka
Dopiero zaczynasz programować oprogramowanie? Najpierw zacznij od samouczków Wprowadzenie . Przed użyciem wyrażeń lambda należy rozwijać podstawowe umiejętności dotyczące typów i metod.
Czasami chcesz przekazać mały element zachowania, funkcję bezpośrednio do innej metody. Możesz na przykład filtrować listę, ale warunek filtrowania zmienia się w zależności od sytuacji. Zamiast pisać oddzielną metodę nazwaną dla każdego możliwego warunku, należy przekazać sam warunek jako argument.
Wyrażenia lambda to funkcja języka C#, która umożliwia to. Wyrażenie lambda jest kompaktową funkcją śródliniową, którą piszesz bez podawania jej nazwy. Użyj operatora => strzałki, aby oddzielić listę parametrów od treści:
x => x * 2
Odczyt od lewej do prawej: x jest parametrem wejściowym, => oznacza "przechodzi do", i x * 2 jest treścią. Oblicza zwracaną wartość. Jeśli nie ma parametrów lub więcej niż jednego, zawijaj je w nawiasy: () => 42 lub (left, right) => left + right.
Delegaty obsługują wyrażenia lambda
Aby użyć wyrażenia lambda, kompilator języka C# musi znać dwie rzeczy: typy parametrów i typ zwracany. Ten opis, typy parametrów i typ zwracany, jest nazywany typem delegata.
Typ delegata to typ reprezentujący sygnaturę metody. Zmienna typu delegata może przechowywać dowolną zgodną metodę, taką jak wyrażenie lambda lub nazwana metoda, o ile jego typy parametrów i zwracany typ są zgodne.
Zadeklarujesz typ delegata za pomocą słowa kluczowego delegate :
delegate int Transform(int value);
Ta deklaracja mówi: "Transform jest typem delegata dla metod, które akceptują jeden int i zwracają int." Następnie można przypisać wyrażenie lambda lub nazwaną metodę do zmiennej tego typu.
Transform doubler = x => x * 2; // assign a lambda expression
Transform squarer = Square; // assign a named method
Console.WriteLine(doubler(5)); // 10
Console.WriteLine(squarer(5)); // 25
static int Square(int value) => value * value;
Zarówno, doubler jak i squarer przechowują wartość typu Transform. Wywołujesz je dokładnie tak, jak zwykłe metody. Kompilator sprawdza, czy przypisana wartość jest zgodna z zadeklarowaną sygnaturą.
Wbudowane typy delegatów: Func i Action
Deklarowanie niestandardowego typu delegata dla każdej sytuacji może być powtarzalne. .NET zapewnia dwie rodziny typów delegatów ogólnych, Func i Action, które obejmują większość scenariuszy, więc rzadko trzeba użyć delegate słowo kluczowe samodzielnie.
Obie rodziny występują w wersjach z od zera do szesnastu parametrów typu wejściowego, więc skalują się do dowolnej liczby danych wejściowych. Kluczową różnicą między dwiema rodzinami jest:
-
System.Func<T,TResult> (i
Func<T1, T2, TResult>itd.) reprezentuje metodę zwracającą wartość. Ostatni parametr typu jest zawsze typem zwracanym, wszystkie wcześniejsze są parametrami wejściowymi. -
System.Action<T> (i
Action<T1, T2>itd.) reprezentuje metodę, która nie zwraca niczego (void). Wszystkie parametry typu są typami wejściowymi. System.Action bez parametrów typu reprezentuje metodę bez danych wejściowych i bez zwracanej wartości.
Na przykład Func<int, int, int> opisuje metodę z dwoma int danymi wejściowymi i jednym int wynikiem.
Action<string> opisuje metodę z jednym string wejściem i bez zwracanej wartości.
Func<int, int, int> add = (left, right) => left + right;
Action<string> report = message => Console.WriteLine($"Report: {message}");
int total = add(5, 9);
report($"5 + 9 = {total}");
Użyj opisowych nazw parametrów w wyrażeniach lambda, aby czytelnicy mogli zrozumieć intencję bez skanowania pełnej treści metody.
Przekazywanie wyrażenia lambda do metody
Gdy metoda deklaruje parametr Func lub Action, wywołujące przekazują wyrażenie lambda zgodne z tym typem delegata. Kompilator sprawdza, czy typy parametrów lambda i typ zwracany są zgodne z zadeklarowanym typem delegata. Jeśli nie są one zgodne, kod nie zostanie skompilowany.
int[] numbers = [1, 2, 3, 4, 5, 6];
int[] evenNumbers = Filter(numbers, value => value % 2 == 0).ToArray();
Console.WriteLine(string.Join(", ", evenNumbers));
Metoda Filter deklaruje Func<int, bool> parametr o nazwie predicate. Typ Func<int, bool> informuje wywołującego o oczekiwanym kształcie: jedno int wejście, jeden bool wynik. Obiekt wywołujący przekazuje value => value % 2 == 0 jako argument. Ten wzorzec pojawia się w wielu interfejsach API LINQ i wielu interfejsach API .NET.
Zachowaj niezależne wyrażenia lambda
Wyrażenie lambda może odwoływać się do zmiennych z otaczającego kodu. Przechwytywanie oznacza, że lambda przechowuje odwołanie do zmiennej zadeklarowanej poza własną treścią. Kombinacja lambda i przechwyconych zmiennych jest nazywana zamknięciem.
Jeśli nie musisz nic przechwytywać, dodaj modyfikator static do wyrażenia lambda. Statyczna lambda może używać tylko własnych parametrów i wartości zadeklarowanych wewnątrz jej treści. Nie może przechwytywać zmiennych lokalnych ani stanu wystąpienia z otaczającego zakresu.
Func<int, bool> isEven = static value => value % 2 == 0;
Console.WriteLine(isEven(14));
Console.WriteLine(isEven(15));
Statyczne lambdy jasno wyjaśniają intencję i zapobiegają przypadkowemu przechwytywaniu.
Użyj parametrów odrzucania, gdy dane wejściowe są nieistotne
Czasami podpis delegata zawiera parametry, których nie potrzebujesz. Użyj odrzucenia _ , aby jawnie zasygnalizować ten wybór.
pl-PL: Typowe przykłady obejmują obsługę zdarzeń, gdzie nie używasz sender lub EventArgs, wywołania zwrotne, w których potrzebujesz tylko niektórych z kilku danych wejściowych, oraz przeciążenia LINQ, które oferują indeks, którego nie używasz.
Action<int, int, string> statusUpdate = (_, _, message) => Console.WriteLine(message);
statusUpdate(200, 42, "Operation completed");
Odrzuty poprawiają czytelność, gdyż pokazują, które parametry są istotne.
Zdarzenia zapewniają opcjonalne powiadomienia
Zdarzenie jest mechanizmem używanym przez jeden obiekt (wydawca) do powiadamiania innych obiektów (subskrybentów) w przypadku wystąpienia czegoś. Wydawca nie musi wiedzieć, kto nasłuchuje ani ilu subskrybentów istnieje. Subskrybenci wybierają opcję uczestnictwa.
Zdarzenia są tworzone na delegatach. Zdarzenie to pole typu delegat z dodatkowymi ograniczeniami wymuszonymi przez słowo kluczowe event: kod zewnętrzny może tylko subskrybować (+=) lub anulować subskrypcję (-=) zdarzenia; tylko klasa, która deklaruje zdarzenie, może je wywołać (podnieść).
Konwencja .NET dla typów delegatów zdarzeń to System.EventHandler<TEventArgs>, gdzie T jest typem danych zawartych w powiadomieniu. Jego podpis zawsze ma dwa parametry: sender (obiekt, który zgłosił zdarzenie) i dane zdarzenia typu T.
MessagePublisher publisher = new();
publisher.MessagePublished += (_, message) => Console.WriteLine($"Received: {message}");
publisher.Publish("Records updated");
Przechodzenie przez kod:
-
MessagePublisherevent EventHandler<string>? MessagePublisheddeklaruje. Słowoeventkluczowe oznacza, że osoby wywołujące mogą subskrybować lub anulować subskrypcji — nie mogą wywoływać ich bezpośrednio. -
publisher.MessagePublished += (_, message) => ...subskrybuje korzystając z wyrażenia lambda._odrzuca parametrsenderponieważ ten handler go nie potrzebuje. -
publisher.Publish("Records updated")zgłasza zdarzenie i uruchamia każdą subskrybowaną procedurę obsługującą.
Subskrybowanie jest opcjonalne. Metoda ?.Invoke(...) w metodzie Publish oznacza, że zdarzenie jest wywoływane tylko wtedy, gdy co najmniej jeden subskrybent jest dołączony. Wydawca zgłasza wydarzenie bez znajomości lub dbania o to, czy ktoś słucha.