Espressioni Lambda C #: Perché dovrei usarle?


310

Ho letto rapidamente 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 finisce la "variabile metodo", giusto? Perché dovrei usare questo invece di un metodo reale?


3
Per quelli di voi che vengono su questa pagina e non sanno cosa delegatec'è in C #, consiglio vivamente di leggerlo prima di leggere il resto di questa pagina: stackoverflow.com/questions/2082615/…
Kolob Canyon

Risposte:


282

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

Di seguito è riportato un esempio di espressione LINQ to Objects che utilizza delegati anonimi quindi espressioni lambda per mostrare quanto sono più facili da vedere:

// 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 il vantaggio di scrivere 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 delle espressioni sono una nuova funzionalità molto potente di C # 3.0 che consente a un'API di esaminare la struttura di un'espressione invece di ottenere semplicemente un riferimento a un metodo che può essere eseguito. Un'API deve solo trasformare un parametro delegato in un Expression<T>parametro e il compilatore genererà un albero delle espressioni da un lambda anziché da un delegato anonimo:

void Example(Predicate<int> aDelegate);

chiamato come:

Example(x => x > 5);

diventa:

void Example(Expression<Predicate<int>> expressionTree);

A quest'ultimo verrà passata una rappresentazione dell'albero di sintassi astratto che descrive l'espressione x > 5. LINQ to SQL si basa su questo comportamento per poter trasformare le espressioni C # nelle espressioni SQL desiderate per il filtro / ordinamento / ecc. Sul lato server.


1
Senza chiusure è possibile utilizzare metodi statici come callback, ma è comunque necessario definire tali metodi in alcune classi, aumentando quasi sicuramente l'ambito di tale metodo oltre l'uso previsto.
DK.

10
FWIW, puoi avere chiusure con un delegato anonimo, quindi non hai assolutamente bisogno di lambda per quello. I lambda sono semplicemente più leggibili dei delegati anonimi, senza i quali l'uso di Linq ti farebbe sanguinare gli occhi.
Benjol,

138

Le funzioni e le espressioni anonime sono utili per i metodi unici 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<string> persons)
 {
     foreach (string person in persons)
         if (person.Contains(nameContains))
             return person;
     return null;
 }

Questi sono funzionalmente equivalenti.


8
Come sarebbe stato definito il metodo Find () per gestire questa espressione lambda?
Patrick Desjardins,

3
Il predicato <T> è ciò che il metodo Find si aspetta.
Darren Kopp,

1
Poiché la mia espressione lambda corrisponde al contratto per Predicate <T>, il metodo Find () lo accetta.
Joseph Daigle,

intendevi "string person = people.Find (people => persons.Contains (" Joe "));"
Gern Blanston,

5
@FKCoder, no, non lo fa, anche se avrebbe potuto essere più chiaro se avesse detto "string person = people.Find (p => p.Contains (" Joe "));"
Benjol,

84

Li ho trovati utili in una situazione in cui volevo dichiarare un gestore per un evento di controllo, usando un altro controllo. Per farlo normalmente dovresti archiviare i riferimenti dei controlli nei campi della classe in modo da poterli usare 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.


Nel primo esempio, perché non trasmettere mittente e ottenere il valore?
Andrew,

@Andrew: in questo semplice esempio non è necessario utilizzare il mittente, poiché esiste un solo componente in questione e l'utilizzo del campo salva direttamente un cast, migliorando la chiarezza. In uno scenario del mondo reale, preferirei personalmente utilizzare anche il mittente. Di solito uso un gestore di eventi per diversi eventi, se possibile, quindi devo identificare il mittente effettivo.
Chris Tophski,

35

Lambda ha ripulito la sintassi del delegato anonimo di C # 2.0 ... per esempio

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

È stato fatto in C # 2.0 in questo modo:

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

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


3
Non sono esattamente la stessa cosa - come sottolinea @Neil Williams, è possibile estrarre l'AST di una lambda usando gli alberi delle espressioni, mentre i metodi anonimi non possono essere usati allo stesso modo.
ljs

questo è uno dei tanti altri vantaggi di lambda. Aiuta a capire il codice meglio dei metodi anonimi. sicuramente non è intenzione di creare lambdas ma questi sono scenari in cui può essere utilizzato più spesso.
Guruji,

29

Questo è solo un modo di usare un'espressione lambda. È possibile utilizzare un'espressione lambda ovunque sia possibile utilizzare un delegato. Questo ti permette di fare cose come questa:

List<string> strings = new List<string>();
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à nell'elenco una voce che corrisponde alla parola "ciao". L'altro modo per farlo è in realtà passare un delegato al metodo Find, in questo modo:

List<string> strings = new List<string>();
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 sintassi del delegato anonimo:

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

Lambda ha ripulito in modo significativo questa sintassi.


2
@Jonathan Holland: grazie per la modifica e l'aggiunta della sintassi del delegato anonimo. Completa bene l'esempio.
Scott Dorman,

cos'è un delegato anonimo? // scusa, sono nuovo a c #
HackerMan,

1
@HackerMan, pensa a un delegato anonimo come una funzione che non ha un "nome". Stai ancora definendo una funzione, che può avere input e output, ma dal momento che è un nome non puoi farvi riferimento direttamente. Nel codice mostrato sopra, stai definendo un metodo (che accetta a stringe restituisce a bool) come parametro al Findmetodo stesso.
Scott Dorman,

22

Microsoft ci ha fornito un modo più pulito e 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 delle espressioni 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<Func<int, int>> 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 compilarla in fase di esecuzione". E avresti ragione. Ma questo fornisce le basi per gli alberi delle espressioni. Esistono diverse espressioni disponibili negli spazi dei nomi delle espressioni e puoi crearne di tue. Penso che puoi vedere che questo potrebbe essere utile quando non sai esattamente quale dovrebbe essere l'algoritmo in fase di progettazione o compilazione. Ho visto un esempio da qualche parte per usarlo per scrivere una calcolatrice scientifica. Potresti anche usarlo per i sistemi bayesiani o per la programmazione genetica(AI). Alcune volte nella mia carriera ho dovuto scrivere funzionalità simili a Excel che consentivano agli utenti di inserire espressioni semplici (addizioni, sottrazioni, ecc.) Per operare sui dati disponibili. In pre.Net 3.5 ho dovuto ricorrere ad un linguaggio di scripting esterno a C # o ho dovuto utilizzare la funzionalità di emissione del codice in riflessione per creare il codice .Net al volo. Ora userei gli alberi delle espressioni.


12

Si evita di dover avere metodi che vengono utilizzati una sola volta in un luogo specifico dall'essere definiti lontano dal luogo in cui vengono utilizzati. I buoni usi sono come comparatori per algoritmi generici come l'ordinamento, in cui è quindi possibile definire una funzione di ordinamento personalizzata in cui si sta invocando l'ordinamento piuttosto che allontanarsi più lontano costringendoti a cercare altrove per vedere su cosa stai ordinando.

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


6

Puoi anche trovare l'uso delle espressioni lambda nella scrittura di codici generici per agire sui tuoi metodi.

Ad esempio: funzione generica per calcolare il tempo impiegato da una chiamata di metodo. (cioè Actionqui)

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));

Expression ti consente di ottenere il valore di ritorno dal tuo metodo e anche da parametro

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

5

L'espressione lambda è un modo conciso di 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 parametro 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 inferire il tipo di variabile in base al contesto. È molto conveniente perché questo ci consente di risparmiare molta digitazione!


5

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<string, string> strategy)
    {
        return strategy("Lijo").ToString();
    }

Riferimenti

  1. In che modo un delegato e un'interfaccia possono essere usati in modo intercambiabile?

4

Molte volte, stai usando la funzionalità solo in un posto, quindi creare un metodo ingombra la classe.


3

È un modo per eseguire piccole operazioni e metterlo molto vicino a dove viene utilizzato (non diversamente da dichiarare una variabile vicino al suo punto d'uso). Questo dovrebbe rendere il tuo codice più leggibile. Anonimizzando l'espressione, stai anche rendendo molto più difficile per qualcuno rompere il tuo codice client se la funzione viene utilizzata da qualche altra parte e modificata per "migliorarla".

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


0

L'innovazione è nel tipo sicurezza e trasparenza. Sebbene non dichiariate i tipi di espressioni lambda, vengono dedotte e possono essere utilizzate dalla ricerca del codice, dall'analisi statica, dagli strumenti di refactoring e dalla riflessione del runtime.

Ad esempio, prima di poter usare SQL e ottenere un attacco di iniezione SQL, perché un hacker ha passato una stringa in cui normalmente era previsto un numero. Ora useresti un'espressione lambda LINQ, che è protetta da quella.

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

Nel 2016 la maggior parte delle lingue popolari ha il supporto dell'espressione lambda e C # è stato uno dei pionieri di questa evoluzione tra le lingue imperative tradizionali.


0

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

In sintesi, è per migliorare la leggibilità del codice, ridurre le possibilità di errori riutilizzando piuttosto che replicare il codice e sfruttare l'ottimizzazione che sta avvenendo dietro le quinte.


0

Il più grande vantaggio delle espressioni lambda e delle funzioni anonime è il fatto che consentono al client (programmatore) di una libreria / framework di iniettare funzionalità tramite il codice nella libreria / framework forniti (in quanto è LINQ, ASP.NET Core e molti altri) in un modo che i metodi regolari non possono. Tuttavia, il loro punto di forza non è evidente per un singolo programmatore di applicazioni, ma per quello che crea librerie che verranno successivamente utilizzate da altri che vorranno configurare il comportamento del codice della libreria o quello che utilizza le librerie. Quindi il contesto dell'uso efficace di un'espressione lambda è l'uso / la creazione di una libreria / framework.

Inoltre, poiché descrivono il codice di utilizzo una tantum, non devono essere membri di una classe in cui ciò comporterà una maggiore complessità del codice. Immagina di dover dichiarare una classe con un focus poco chiaro ogni volta che volevamo configurare il funzionamento di un oggetto di classe.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.