Espressioni di Lambda C #: perché dovrei usarle?

Ho letto velocemente la documentazione di Microsoft Lambda Expression .

Questo tipo di esempio mi ha aiutato a capire meglio, però:

delegate int del(int i); del myDelegate = x => x * x; int j = myDelegate(5); //j = 25 

Tuttavia, non capisco perché sia ​​una tale innovazione. È solo un metodo che muore quando termina la “variabile del metodo”, giusto? Perché dovrei usare questo invece di un metodo reale?

Le espressioni Lambda sono una syntax più semplice per i delegati anonimi e possono essere utilizzate ovunque sia ansible utilizzare un delegato anonimo. Tuttavia, il contrario non è vero; le espressioni lambda possono essere convertite in alberi di espressione che consentono un sacco di magia come LINQ to SQL.

Di seguito è riportato un esempio di un’espressione LINQ to Objects che utilizza delegati anonimi e espressioni lambda per mostrare quanto siano più semplici per gli occhi:

 // anonymous delegate var evens = Enumerable .Range(1, 100) .Where(delegate(int x) { return (x % 2) == 0; }) .ToList(); // lambda expression var evens = Enumerable .Range(1, 100) .Where(x => (x % 2) == 0) .ToList(); 

Le espressioni Lambda e i delegati anonimi hanno un vantaggio rispetto alla scrittura di una funzione separata: implementano chiusure che possono consentire di passare lo stato locale alla funzione senza aggiungere parametri alla funzione o creare oggetti monouso.

Gli alberi di espressione sono una nuova funzionalità molto potente di C # 3.0 che consente a un’API di esaminare la struttura di un’espressione anziché ottenere semplicemente un riferimento a un metodo che può essere eseguito. Un’API deve semplicemente creare un parametro delegato in un parametro Expression e il compilatore genererà un albero di espressioni da un lambda anziché da un delegato anonimo:

 void Example(Predicate aDelegate); 

chiamato come:

 Example(x => x > 5); 

diventa:

 void Example(Expression> expressionTree); 

Quest’ultimo riceverà una rappresentazione dell’albero di syntax astratto che descrive l’espressione x > 5 . LINQ to SQL si basa su questo comportamento per essere in grado di trasformare le espressioni C # nelle espressioni SQL desiderate per il filtraggio / ordinamento / ecc. Sul lato server.

Le funzioni e le espressioni anonime sono utili per i metodi one-off che non beneficiano del lavoro extra richiesto per creare un metodo completo.

Considera questo esempio:

  string person = people.Find(person => person.Contains("Joe")); 

contro

  public string FindPerson(string nameContains, List persons) { foreach (string person in persons) if (person.Contains(nameContains)) return person; return null; } 

Questi sono funzionalmente equivalenti.

Li ho trovati utili in una situazione in cui volevo dichiarare un gestore per l’evento di qualche controllo, usando un altro controllo. Per farlo normalmente dovresti memorizzare i riferimenti dei controlli nei campi della class in modo che tu possa usarli in un metodo diverso da quello in cui sono stati creati.

 private ComboBox combo; private Label label; public CreateControls() { combo = new ComboBox(); label = new Label(); //some initializing code combo.SelectedIndexChanged += new EventHandler(combo_SelectedIndexChanged); } void combo_SelectedIndexChanged(object sender, EventArgs e) { label.Text = combo.SelectedValue; } 

grazie alle espressioni lambda puoi usarlo in questo modo:

 public CreateControls() { ComboBox combo = new ComboBox(); Label label = new Label(); //some initializing code combo.SelectedIndexChanged += (s, e) => {label.Text = combo.SelectedValue;}; } 

Molto più facile.

Lambda ha ripulito la syntax dei delegati anonimi del C # 2.0 … per esempio

 Strings.Find(s => s == "hello"); 

È stato fatto in C # 2.0 come questo:

 Strings.Find(delegate(String s) { return s == "hello"; }); 

Funzionalmente, fanno esattamente la stessa cosa, è solo una syntax molto più concisa.

Questo è solo un modo per usare un’espressione lambda. Puoi usare un’espressione lambda ovunque tu possa usare un delegato. Questo ti permette di fare cose come questa:

 List strings = new List(); strings.Add("Good"); strings.Add("Morning") strings.Add("Starshine"); strings.Add("The"); strings.Add("Earth"); strings.Add("says"); strings.Add("hello"); strings.Find(s => s == "hello"); 

Questo codice cercherà nella lista una voce che corrisponda alla parola “ciao”. L’altro modo per farlo è in realtà passare un delegato al metodo Find, in questo modo:

 List strings = new List(); strings.Add("Good"); strings.Add("Morning") strings.Add("Starshine"); strings.Add("The"); strings.Add("Earth"); strings.Add("says"); strings.Add("hello"); private static bool FindHello(String s) { return s == "hello"; } strings.Find(FindHello); 

MODIFICA :

In C # 2.0, questo potrebbe essere fatto usando la syntax dei delegati anonimi:

  strings.Find(delegate(String s) { return s == "hello"; }); 

Lambda ha ripulito in modo significativo questa syntax.

Microsoft ci ha dato un modo più pulito e più conveniente di creare delegati anonimi chiamati espressioni Lambda. Tuttavia, non viene prestata molta attenzione alla parte relativa alle espressioni di questa affermazione. Microsoft ha rilasciato un intero spazio dei nomi, System.Linq.Expressions , che contiene classi per creare alberi di espressioni basati su espressioni lambda. Gli alberi di espressione sono costituiti da oggetti che rappresentano la logica. Ad esempio, x = y + z è un’espressione che potrebbe far parte di un albero di espressioni in .Net. Considera il seguente esempio (semplice):

 using System; using System.Linq; using System.Linq.Expressions; namespace ExpressionTreeThingy { class Program { static void Main(string[] args) { Expression> expr = (x) => x + 1; //this is not a delegate, but an object var del = expr.Compile(); //compiles the object to a CLR delegate, at runtime Console.WriteLine(del(5)); //we are just invoking a delegate at this point Console.ReadKey(); } } } 

Questo esempio è banale. E sono sicuro che stai pensando: “Questo è inutile in quanto avrei potuto creare direttamente il delegato invece di creare un’espressione e compilarlo in fase di runtime”. E avresti ragione. Ma questo fornisce la base per gli alberi di espressione. Ci sono un certo numero di espressioni disponibili negli spazi dei nomi di Espressioni e puoi costruirne di tue. Penso che tu possa vedere che questo potrebbe essere utile quando non sai esattamente quale dovrebbe essere l’algoritmo al momento della progettazione o della compilazione. Ho visto un esempio da qualche parte per usarlo per scrivere un calcolatore scientifico. Potresti usarlo anche per i sistemi bayesiani o per la programmazione genetica (AI). Alcune volte nella mia carriera ho dovuto scrivere una funzionalità simile a Excel che consentiva agli utenti di inserire espressioni semplici (aggiunta, sottrazione, ecc.) Per operare sui dati disponibili. In pre-.Net 3.5 ho dovuto ricorrere a qualche linguaggio di scripting esterno a C #, o ho dovuto usare la funzionalità di emettere codice in reflection per creare codice .Net al volo. Ora userei gli alberi di espressione.

Si risparmia il dover disporre di metodi che vengono utilizzati solo una volta in un luogo specifico, da essere definiti lontano dal luogo in cui vengono utilizzati. Buoni usi sono come comparatori per algoritmi generici come l’ordinamento, in cui è ansible quindi definire una funzione di ordinamento personalizzata in cui si sta invocando l’ordinamento piuttosto che allontanarsi ulteriormente costringendo a cercare altrove per vedere cosa si sta ordinando.

E non è davvero un’innovazione. LISP ha avuto funzioni lambda per circa 30 anni o più.

Un’espressione lambda è come un metodo anonimo scritto al posto di un’istanza delegata.

 delegate int MyDelagate (int i); MyDelagate delSquareFunction = x => x * x; 

Considera l’espressione lambda x => x * x;

Il valore del parametro di input è x (sul lato sinistro di =>)

La logica della funzione è x * x (sul lato destro di =>)

Il codice di un’espressione lambda può essere un blocco di istruzioni anziché un’espressione.

 x => {return x * x;}; 

Esempio

Nota: Func è un delegato generico predefinito.

  Console.WriteLine(MyMethod(x => "Hi " + x)); public static string MyMethod(Func strategy) { return strategy("Lijo").ToString(); } 

Riferimenti

  1. In che modo un delegato e un’interfaccia possono essere utilizzati in modo intercambiabile?

Puoi anche trovare l’uso delle espressioni lambda nello scrivere codici generici per agire sui tuoi metodi.

Ad esempio: Funzione generica per calcolare il tempo impiegato da una chiamata di metodo. (cioè Action qui dentro)

 public static long Measure(Action action) { Stopwatch sw = new Stopwatch(); sw.Start(); action(); sw.Stop(); return sw.ElapsedMilliseconds; } 

E puoi chiamare il metodo sopra usando l’espressione lambda come segue,

 var timeTaken = Measure(() => yourMethod(param)); 

Espressione ti consente di ottenere valore di ritorno dal tuo metodo e anche param

 var timeTaken = Measure(() => returnValue = yourMethod(param, out outParam)); 

Molte volte, si sta utilizzando la funzionalità in un unico posto, quindi un metodo semplifica la class.

L’espressione lambda è un modo conciso per rappresentare un metodo anonimo. Sia i metodi anonimi che le espressioni Lambda consentono di definire l’implementazione del metodo in linea, tuttavia, un metodo anonimo richiede esplicitamente di definire i tipi di parametri e il tipo restituito per un metodo. L’espressione Lambda utilizza la funzione di inferenza del tipo di C # 3.0 che consente al compilatore di dedurre il tipo di variabile in base al contesto. È molto comodo perché ci risparmia molto digitando!

È un modo di fare piccole operazioni e metterlo molto vicino a dove viene usato (non diversamente dalla dichiarazione di una variabile vicino al suo punto di utilizzo). Questo dovrebbe rendere il tuo codice più leggibile. Anonimizzando l’espressione, stai rendendo molto più difficile a qualcuno di violare il codice client se la funzione viene utilizzata altrove e modificata per “migliorarla”.

Allo stesso modo, perché devi usare foreach? Puoi fare tutto in foreach con un ciclo semplice o semplicemente usando direttamente IEnumerable. Risposta: non ne hai bisogno ma rende il tuo codice più leggibile.

L’innovazione è nel tipo sicurezza e trasparenza. Sebbene non si dichiarino tipi di espressioni lambda, questi vengono dedotti e possono essere utilizzati dalla ricerca del codice, dall’analisi statica, dagli strumenti di refactoring e dalla riflessione runtime.

Ad esempio, prima potresti aver usato SQL e ottenere un attacco di SQL injection, perché un hacker ha passato una stringa in cui normalmente era previsto un numero. Ora dovresti usare un’espressione lambda LINQ, che è protetta da quello.

La creazione di un’API LINQ su delegati puri non è ansible, poiché richiede la combinazione di alberi di espressioni prima di valutarli.

Nel 2016 la maggior parte delle lingue popolari ha il supporto per le espressioni lambda , e C # è stato uno dei pionieri di questa evoluzione tra le lingue imperative mainstream.

Questa è forse la migliore spiegazione sul perché usare espressioni lambda -> https://youtu.be/j9nj5dTo54Q

In breve, è per migliorare la leggibilità del codice, ridurre le possibilità di errori riutilizzando anziché replicare il codice e sfruttare l’ottimizzazione che avviene dietro le quinte.