Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Tip
Novità dello sviluppo di software? Iniziare prima con le esercitazioni introduttive . Prima di usare espressioni lambda, è necessario sviluppare competenze di base sui tipi e i metodi.
A volte si vuole passare una piccola parte di comportamento, una funzione, direttamente a un altro metodo. Ad esempio, è possibile filtrare un elenco, ma la condizione di filtro cambia a seconda della situazione. Anziché scrivere un metodo denominato separato per ogni condizione possibile, passare la condizione stessa come argomento.
Le espressioni lambda sono la funzionalità C# che rende possibile questa operazione. Un'espressione lambda è una funzione compatta e inline scritta senza assegnargli un nome. Si utilizza l'operatore freccia => con per separare l'elenco di parametri dal corpo:
x => x * 2
Lettura da sinistra a destra: x è il parametro di input, => indica "va a", e x * 2 è il corpo. Calcola il valore restituito. Quando non è presente alcun parametro o ne sono presenti più di uno, avvolgerli tra parentesi: () => 42 o (left, right) => left + right.
I delegati supportano espressioni lambda
Per usare un'espressione lambda, il compilatore C# deve conoscere due aspetti: i tipi dei parametri e il tipo restituito. Tale descrizione, i tipi di parametro più il tipo restituito, viene chiamato tipo delegato.
Un tipo delegato è un tipo che rappresenta una firma del metodo. Una variabile di un tipo delegato può contenere qualsiasi metodo corrispondente, ad esempio un'espressione lambda o un metodo denominato, purché i tipi di parametro e la corrispondenza del tipo restituito corrispondano.
Si dichiara un tipo delegato con la delegate parola chiave :
delegate int Transform(int value);
Questa dichiarazione dice: "Transform è un tipo delegato per i metodi che accettano uno int e restituiscono ". int È quindi possibile assegnare un'espressione lambda o un metodo denominato a una variabile di tale tipo:
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;
Sia doubler che squarer contengono un valore di tipo Transform. Li chiami esattamente come i metodi normali. Il compilatore verifica che qualsiasi elemento assegnato corrisponda alla firma dichiarata.
Tipi delegati predefiniti: Func e Action
La dichiarazione di un tipo delegato personalizzato per ogni situazione può essere ripetitiva. .NET fornisce due famiglie di tipi delegati generici,
Entrambe le famiglie sono disponibili in versioni con da zero a sedici parametri di tipo di input, quindi si adattano a qualsiasi numero di input. La differenza principale tra le due famiglie è:
-
System.Func<T,TResult> (e
Func<T1, T2, TResult>così via) rappresenta un metodo che restituisce un valore. L'ultimo parametro di tipo è sempre il tipo restituito; tutti quelli precedenti sono tipi di input. -
System.Action<T> (e
Action<T1, T2>così via) rappresenta un metodo che non restituisce nulla (void). Tutti i parametri di tipo sono tipi di input. System.Action senza parametri di tipo rappresenta un metodo senza input e nessun valore restituito.
Ad esempio, Func<int, int, int> descrive un metodo con due int input e un int risultato.
Action<string> descrive un metodo con un string input e nessun valore restituito.
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}");
Usare nomi di parametri descrittivi nelle espressioni lambda in modo che i lettori possano comprendere la finalità senza analizzare il corpo completo del metodo.
Passare un'espressione lambda a un metodo
Quando un metodo dichiara un Func parametro o Action , i chiamanti passano un'espressione lambda che corrisponde a tale tipo delegato. Il compilatore verifica che i tipi di parametro lambda e il tipo restituito corrispondano al tipo delegato dichiarato. Se non corrispondono, il codice non viene compilato.
int[] numbers = [1, 2, 3, 4, 5, 6];
int[] evenNumbers = Filter(numbers, value => value % 2 == 0).ToArray();
Console.WriteLine(string.Join(", ", evenNumbers));
Il Filter metodo dichiara un Func<int, bool> parametro denominato predicate. Il tipo Func<int, bool> indica agli utenti la forma prevista: uno int input, uno bool risultato. Il chiamante passa value => value % 2 == 0 come argomento. Questo modello viene visualizzato in LINQ e in molte API di .NET.
Mantenere autonome le espressioni lambda
Un'espressione lambda può fare riferimento a variabili dal codice circostante. L'acquisizione significa che la lambda mantiene un riferimento a una variabile dichiarata al di fuori del proprio corpo. La combinazione dell'espressione lambda e delle variabili acquisite è detta chiusura.
Quando non è necessario acquisire nulla, aggiungere il static modificatore all'espressione lambda. Una funzione lambda statica può usare solo i propri parametri e i valori dichiarati all'interno del proprio blocco di codice. Non può acquisire le variabili locali o lo stato dell'istanza dall'ambito circostante.
Func<int, bool> isEven = static value => value % 2 == 0;
Console.WriteLine(isEven(14));
Console.WriteLine(isEven(15));
Le espressioni lambda statiche rendono chiaro l'intento e impediscono catture accidentali.
Usare i parametri discard quando gli input sono irrilevanti
In alcuni casi una firma del delegato include parametri non necessari. Usare il valore discard _ per segnalare esplicitamente tale scelta.
Gli esempi comuni includono gestori eventi in cui non si usano sender o EventArgs, i callback in cui sono necessari solo alcuni input e overload LINQ che forniscono un indice non usato.
Action<int, int, string> statusUpdate = (_, _, message) => Console.WriteLine(message);
statusUpdate(200, 42, "Operation completed");
Gli scarti migliorano la leggibilità perché mostrano quali parametri sono importanti.
Gli eventi forniscono notifiche facoltative
Un evento è un meccanismo usato da un oggetto (autore) per notificare ad altri oggetti ( sottoscrittori) quando si verifica un evento. L'editore non deve sapere chi è in ascolto o quanti sottoscrittori sono presenti. I sottoscrittori scelgono di partecipare.
Gli eventi sono basati su delegati. Un evento è un campo delegato con restrizioni aggiuntive applicate dal event parola chiave: il codice esterno può solo iscriversi (+=) o disiscriversi (-=) dall'evento. Solo la classe che dichiara l'evento può richiamarlo (generarlo).
La convenzione di .NET per i tipi di delegato di eventi è System.EventHandler<TEventArgs>, dove T è il tipo di dati inclusi nella notifica. La firma ha sempre due parametri: l'oggetto sender (l'oggetto che ha generato l'evento) e i dati dell'evento di tipo T.
MessagePublisher publisher = new();
publisher.MessagePublished += (_, message) => Console.WriteLine($"Received: {message}");
publisher.Publish("Records updated");
Scorrere il codice:
-
MessagePublisherdichiaraevent EventHandler<string>? MessagePublished. Laeventparola chiave indica che i chiamanti possono solo sottoscrivere o annullare la sottoscrizione. Non possono richiamarlo direttamente. -
publisher.MessagePublished += (_, message) => ...si sottoscrive con un'espressione lambda. Scarta_il parametrosenderperché questo gestore non ne ha bisogno. -
publisher.Publish("Records updated")genera l'evento ed esegue ogni gestore registrato.
La sottoscrizione è facoltativa. Nel metodo ?.Invoke(...), Publish significa che l'evento scatta solo quando è associato almeno un sottoscrittore. L'editore attiva l'evento senza sapere o preoccuparsi se qualcuno è in ascolto.