Qual è la differenza tra lambda e delegati in .NET Framework?


88

Mi viene fatta spesso questa domanda e ho pensato di sollecitare un input su come descrivere al meglio la differenza.


2
Per "delegati" intendi tipi di delegati o delegati anonimi? Sono anche diversi.
Chris Ammerman,


1
perché le persone rendono la sua domanda così complicata? Rispondi solo cos'è un delegato e cos'è un lambda. Fornisci quante più spiegazioni possibili e lascia che scelga ciò che è appropriato per lui.
Imir Hoxha

Risposte:


98

In realtà sono due cose molto diverse. "Delegate" è in realtà il nome di una variabile che contiene un riferimento a un metodo o lambda e lambda è un metodo senza un nome permanente.

I lambda sono molto simili agli altri metodi, tranne per un paio di sottili differenze.

  1. Un metodo normale è definito in una "dichiarazione" e legato a un nome permanente, mentre un lambda è definito "al volo" in una "espressione" e non ha un nome permanente.
  2. Alcuni lambda possono essere utilizzati con alberi delle espressioni .NET, mentre i metodi non possono.

Un delegato è definito in questo modo:

delegate Int32 BinaryIntOp(Int32 x, Int32 y);

A una variabile di tipo BinaryIntOp può essere assegnato un metodo o un labmda, purché la firma sia la stessa: due argomenti Int32 e un ritorno Int32.

Un lambda potrebbe essere definito in questo modo:

BinaryIntOp sumOfSquares = (a, b) => a*a + b*b;

Un'altra cosa da notare è che sebbene i tipi generici Func e Action siano spesso considerati "tipi lambda", sono come qualsiasi altro delegato. La cosa bella di loro è che essenzialmente definiscono un nome per qualsiasi tipo di delegato di cui potresti aver bisogno (fino a 4 parametri, anche se puoi sicuramente aggiungerne altri). Pertanto, se si utilizza un'ampia varietà di tipi di delegato, ma nessuno più di una volta, è possibile evitare di ingombrare il codice con le dichiarazioni dei delegati utilizzando Func e Action.

Ecco un'illustrazione di come Func e Action sono "non solo per lambda":

Int32 DiffOfSquares(Int32 x, Int32 y)
{
  return x*x - y*y;
}

Func<Int32, Int32, Int32> funcPtr = DiffOfSquares;

Un'altra cosa utile da sapere è che i tipi delegati (non i metodi stessi) con la stessa firma ma nomi diversi non verranno implicitamente convertiti tra loro. Ciò include i delegati Func e Action. Tuttavia, se la firma è identica, puoi eseguire il cast esplicitamente tra di loro.

Fare il possibile ... In C # le funzioni sono flessibili, con l'uso di lambda e delegati. Ma C # non ha "funzioni di prima classe". È possibile utilizzare il nome di una funzione assegnato a una variabile delegato per creare essenzialmente un oggetto che rappresenta quella funzione. Ma è davvero un trucco del compilatore. Se avvii un'istruzione scrivendo il nome della funzione seguito da un punto (cioè prova ad accedere ai membri sulla funzione stessa) scoprirai che non ci sono membri a cui fare riferimento. Nemmeno quelli di Object. Ciò impedisce al programmatore di fare cose utili (e ovviamente potenzialmente pericolose) come l'aggiunta di metodi di estensione che possono essere chiamati su qualsiasi funzione. Il meglio che puoi fare è estendere la classe Delegate stessa, che è sicuramente anche utile, ma non così tanto.

Aggiornamento: vedere anche la risposta di Karg che illustra la differenza tra delegati anonimi e metodi e lambda.

Aggiornamento 2: James Hart fa una nota importante, anche se molto tecnica, che lambda e delegati non sono entità .NET (cioè CLR non ha il concetto di delegato o lambda), ma piuttosto sono costrutti di framework e linguaggio.


Buona spiegazione. Anche se penso che tu intenda "funzioni di prima classe", non "oggetti di prima classe". :)
ibz

1
Hai ragione. Ho avuto la frase strutturata in modo diverso durante la scrittura ("le funzioni C # non sono in realtà oggetti di prima classe") e ho dimenticato di cambiarla. Grazie!
Chris Ammerman,

Un metodo normale è definito in una "istruzione" Un'istruzione è un'azione nella sequenza di un programma imperativo, possibilmente basata su un'espressione. La definizione di un metodo non è una struttura grammaticale diversa? La definizione del metodo non è elencata in docs.microsoft.com/en-us/dotnet/csharp/tour-of-csharp/…
Max Barraclough

32

La domanda è un po 'ambigua, il che spiega l'ampia disparità di risposte che stai ricevendo.

In realtà hai chiesto qual è la differenza tra lambda e delegati nel framework .NET; potrebbe essere una delle tante cose. Stai chiedendo:

  • Qual è la differenza tra espressioni lambda e delegati anonimi nel linguaggio C # (o VB.NET)?

  • Qual è la differenza tra gli oggetti System.Linq.Expressions.LambdaExpression e gli oggetti System.Delegate in .NET 3.5?

  • O qualcosa da qualche parte tra o intorno a questi estremi?

Alcune persone sembrano cercare di darti la risposta alla domanda "qual è la differenza tra le espressioni Lambda C # e .NET System.Delegate?", Il che non ha molto senso.

Il framework .NET di per sé non comprende i concetti di delegati anonimi, espressioni lambda o chiusure: sono tutte cose definite dalle specifiche del linguaggio. Pensa a come il compilatore C # traduce la definizione di un metodo anonimo in un metodo su una classe generata con variabili membro per mantenere lo stato di chiusura; a .NET, non c'è niente di anonimo nel delegato; è solo anonimo per il programmatore C # che lo scrive. Ciò è altrettanto vero per un'espressione lambda assegnata a un tipo delegato.

Che .NET FA capire è l'idea di un delegato - un tipo che descrive una firma del metodo, le istanze di cui rappresentare sia legato chiamate a metodi specifici su oggetti specifici, o chiamate non legati ad un particolare metodo su un tipo particolare, che può essere invocato nei confronti di qualsiasi oggetto di quel tipo, dove detto metodo aderisce a detta firma. Tali tipi ereditano tutti da System.Delegate.

.NET 3.5 introduce anche lo spazio dei nomi System.Linq.Expressions, che contiene classi per la descrizione di espressioni di codice e che può quindi rappresentare anche chiamate associate o non associate a metodi su tipi o oggetti particolari. Le istanze LambdaExpression possono quindi essere compilate in delegati effettivi (per cui un metodo dinamico basato sulla struttura dell'espressione viene codificato e viene restituito un puntatore delegato).

In C # è possibile produrre istanze di tipi System.Expressions.Expression assegnando un'espressione lambda a una variabile di tale tipo, che produrrà il codice appropriato per costruire l'espressione in fase di esecuzione.

Naturalmente, se fossi chiedendo quale sia la differenza tra le espressioni lambda e metodi anonimi in C #, dopo tutto, allora tutto questo è praticamente irelevant, e in tal caso la differenza principale è la brevità, che si china verso i delegati anonimi quando si don' t interessa i parametri e non prevede di restituire un valore e verso lambda quando si vogliono parametri dedotti dal tipo e tipi restituiti.

E le espressioni lambda supportano la generazione di espressioni.


3
Grandi informazioni! Mi hai ispirato ad accendere il riflettore e guardare l'IL. Non sapevo che i lambda risultassero in classi generate, ma ha perfettamente senso ora che ci penso.
Chris Ammerman,

21

Una differenza è che un delegato anonimo può omettere i parametri mentre un lambda deve corrispondere alla firma esatta. Dato:

public delegate string TestDelegate(int i);

public void Test(TestDelegate d)
{}

puoi chiamarlo nei seguenti quattro modi (nota che la seconda riga ha un delegato anonimo che non ha parametri):

Test(delegate(int i) { return String.Empty; });
Test(delegate { return String.Empty; });
Test(i => String.Empty);
Test(D);

private string D(int i)
{
    return String.Empty;
}

Non è possibile passare un'espressione lambda che non ha parametri o un metodo che non ha parametri. Questi non sono ammessi:

Test(() => String.Empty); //Not allowed, lambda must match signature
Test(D2); //Not allowed, method must match signature

private string D2()
{
    return String.Empty;
}

13

I delegati sono equivalenti a puntatori a funzione / puntatori a metodo / callback (fate la vostra scelta), e le lambda sono funzioni anonime praticamente semplificate. Almeno questo è quello che dico alla gente.


Esattamente! Non c'è differenza". Sono due cose intrinsecamente diverse.
ibz

3

Non ho molta esperienza con questo, ma il modo in cui lo descriverei è che un delegato è un wrapper attorno a qualsiasi funzione, mentre un'espressione lambda è essa stessa una funzione anonima.


3

Un delegato è sempre fondamentalmente solo un puntatore a funzione. Un lambda può trasformarsi in un delegato, ma può anche trasformarsi in un albero delle espressioni LINQ. Per esempio,

Func<int, int> f = x => x + 1;
Expression<Func<int, int>> exprTree = x => x + 1;

La prima riga produce un delegato, mentre la seconda produce un albero delle espressioni.


2
È vero, ma la differenza tra loro è che sono due concetti completamente diversi . È come confrontare mele e arance. Vedi la risposta di Dan Shield.
ibz

2

i lambda sono semplicemente zucchero sintattico su un delegato. Il compilatore finisce per convertire i lambda in delegati.

Questi sono gli stessi, credo:

Delegate delegate = x => "hi!";
Delegate delegate = delegate(object x) { return "hi";};

2
nessuno di questi esempi viene compilato. Anche se cambi il nome dell'istanza di Delegateda "delegate", che è una parola chiave.
Steve Cooper,

2

Un delegato è una firma di funzione; qualcosa di simile a

delegate string MyDelegate(int param1);

Il delegato non implementa un corpo.

Lambda è una chiamata di funzione che corrisponde alla firma del delegato. Per il delegato di cui sopra, potresti usare uno qualsiasi di;

(int i) => i.ToString();
(int i) => "ignored i";
(int i) => "Step " + i.ToString() + " of 10";

Il Delegatetipo ha un nome sbagliato, però; la creazione di un oggetto di tipo Delegatecrea effettivamente una variabile che può contenere funzioni, siano esse lambda, metodi statici o metodi di classe.


Quando crei una variabile di tipo MyDelegate, questo non è realmente di tipo runtime. Il tipo di runtime è Delegato. Ci sono trucchi del compilatore coinvolti nel modo in cui vengono compilati i delegati, i lambda e gli alberi delle espressioni, che penso perché il codice implica cose che non sono vere.
Chris Ammerman,

2

Un delegato è un riferimento a un metodo con un particolare elenco di parametri e un tipo restituito. Può includere o meno un oggetto.

Un'espressione lambda è una forma di funzione anonima.


2

Un delegato è una coda di puntatori a funzione, la chiamata di un delegato può richiamare più metodi. Un lambda è essenzialmente una dichiarazione di metodo anonimo che può essere interpretata dal compilatore in modo diverso, a seconda del contesto in cui viene utilizzato.

È possibile ottenere un delegato che punta all'espressione lambda come metodo eseguendo il cast in un delegato oppure, se lo si passa come parametro a un metodo che prevede un tipo di delegato specifico, il compilatore lo eseguirà per te. Usandolo all'interno di un'istruzione LINQ, lambda verrà tradotto dal compilatore in un albero delle espressioni anziché semplicemente in un delegato.

La differenza è che un lambda è un modo conciso per definire un metodo all'interno di un'altra espressione, mentre un delegato è un tipo di oggetto effettivo.


2

È abbastanza chiaro che la domanda doveva essere "qual è la differenza tra lambdas e anonymous delegati ?" Di tutte le risposte qui, solo una persona ha capito bene: la differenza principale è che i lambda possono essere usati per creare alberi delle espressioni oltre che delegati.

Puoi leggere di più su MSDN: http://msdn.microsoft.com/en-us/library/bb397687.aspx


1

I delegati sono in realtà solo caratteri strutturali per le funzioni. Potresti fare la stessa cosa con la digitazione nominale e l'implementazione di una classe anonima che implementa un'interfaccia o una classe astratta, ma che finisce per essere un sacco di codice quando è necessaria solo una funzione.

Lambda nasce dall'idea del lambda calcolo della chiesa di Alonzo negli anni '30. È un modo anonimo di creare funzioni. Diventano particolarmente utili per comporre funzioni

Quindi, mentre alcuni potrebbero dire che lambda è zucchero sintattico per i delegati, direi che i delegati sono un ponte per facilitare le persone a utilizzare lambda in c #.


1

Alcuni di base qui. "Delegato" è in realtà il nome di una variabile che contiene un riferimento a un metodo o un lambda

Questo è un metodo anonimo -

(string testString) => { Console.WriteLine(testString); };

Poiché il metodo anonimo non ha alcun nome, abbiamo bisogno di un delegato in cui possiamo assegnare entrambi questi metodi o espressioni. Per es.

delegate void PrintTestString(string testString); // declare a delegate

PrintTestString print = (string testString) => { Console.WriteLine(testString); }; 
print();

Lo stesso con l'espressione lambda. Di solito abbiamo bisogno di un delegato per usarli

s => s.Age > someValue && s.Age < someValue    // will return true/false

Possiamo usare un delegato func per usare questa espressione.

Func< Student,bool> checkStudentAge = s => s.Age > someValue && s.Age < someValue ;

bool result = checkStudentAge ( Student Object);

0

Lambda sono versioni semplificate dei delegati. Hanno alcune delle proprietà di una chiusura come i delegati anonimi, ma consentono anche di utilizzare la digitazione implicita. Una lambda come questa:

something.Sort((x, y) => return x.CompareTo(y));

è molto più conciso di quello che puoi fare con un delegato:

something.Sort(sortMethod);
...

private int sortMethod(SomeType one, SomeType two)
{
    one.CompareTo(two)
}

Vuoi dire che i lambda sono come metodi anonimi semplificati (non delegati). Come i metodi (anonimi o meno), possono essere assegnati a una variabile delegato.
Lucas

0

Ecco un esempio che ho messo un po 'sul mio blog zoppo. Supponi di voler aggiornare un'etichetta da un thread di lavoro. Ho 4 esempi di come aggiornare quell'etichetta da 1 a 50 usando delegati, delegati anon e 2 tipi di lambda.

 private void button2_Click(object sender, EventArgs e) 
     { 
         BackgroundWorker worker = new BackgroundWorker(); 
         worker.DoWork += new DoWorkEventHandler(worker_DoWork); 
         worker.RunWorkerAsync(); 
     } 

     private delegate void UpdateProgDelegate(int count); 
     private void UpdateText(int count) 
     { 
         if (this.lblTest.InvokeRequired) 
         { 
             UpdateProgDelegate updateCallBack = new UpdateProgDelegate(UpdateText); 
             this.Invoke(updateCallBack, new object[] { count }); 
         } 
         else 
         { 
             lblTest.Text = count.ToString(); 
         } 
     } 

     void worker_DoWork(object sender, DoWorkEventArgs e) 
     {   
         /* Old Skool delegate usage.  See above for delegate and method definitions */ 
         for (int i = 0; i < 50; i++) 
         { 
             UpdateText(i); 
             Thread.Sleep(50); 
         } 

         // Anonymous Method 
         for (int i = 0; i < 50; i++) 
         { 
             lblTest.Invoke((MethodInvoker)(delegate() 
             { 
                 lblTest.Text = i.ToString(); 
             })); 
             Thread.Sleep(50); 
         } 

         /* Lambda using the new Func delegate. This lets us take in an int and 
          * return a string.  The last parameter is the return type. so 
          * So Func<int, string, double> would take in an int and a string 
          * and return a double.  count is our int parameter.*/ 
         Func<int, string> UpdateProgress = (count) => lblTest.Text = count.ToString(); 
         for (int i = 0; i < 50; i++) 
         { 
             lblTest.Invoke(UpdateProgress, i); 
             Thread.Sleep(50); 
         } 

         /* Finally we have a totally inline Lambda using the Action delegate 
          * Action is more or less the same as Func but it returns void. We could 
          * use it with parameters if we wanted to like this: 
          * Action<string> UpdateProgress = (count) => lblT…*/ 
         for (int i = 0; i < 50; i++) 
         { 
             lblTest.Invoke((Action)(() => lblTest.Text = i.ToString())); 
             Thread.Sleep(50); 
         } 
     }

0

Presumo che la tua domanda riguardi c # e non .NET, a causa dell'ambiguità della tua domanda, dato che .NET non si fa da solo, cioè senza c #, comprensione dei delegati e delle espressioni lambda.

Un delegato ( normale , in opposizione ai cosiddetti delegati generici , cf più tardi) dovrebbe essere visto come una sorta di c ++ typedefdi un tipo di puntatore a funzione, ad esempio in c ++:

R (*thefunctionpointer) ( T ) ;

typedef è il tipo thefunctionpointerche è il tipo di puntatori a una funzione che prende un oggetto di tipo Te restituisce un oggetto di tipo R. Lo useresti in questo modo:

thefunctionpointer = &thefunction ;
R r = (*thefunctionpointer) ( t ) ; // where t is of type T

dove thefunctionsarebbe una funzione che prende Te restituisce un R.

In c # andresti per

delegate R thedelegate( T t ) ; // and yes, here the identifier t is needed

e lo useresti in questo modo:

thedelegate thedel = thefunction ;
R r = thedel ( t ) ; // where t is of type T

dove thefunctionsarebbe una funzione che prende Te restituisce un R. Questo è per i delegati, i cosiddetti delegati normali.

Ora, hai anche delegati generici in c #, che sono delegati che sono generici, cioè che sono "modellati" per così dire, usando quindi un'espressione c ++. Sono definiti così:

public delegate TResult Func<in T, out TResult>(T arg);

E puoi usarli in questo modo:

Func<double, double> thefunctor = thefunction2; // call it a functor because it is
                                                // really as a functor that you should
                                                // "see" it
double y = thefunctor(2.0);

dove thefunction2è una funzione che prende come argomento e restituisce un file double.

Ora immagina che invece di thefunction2vorrei usare una "funzione" che non è definita da nessuna parte per ora, da un'istruzione, e che non userò mai più tardi. Quindi c # ci permette di usare l' espressione di questa funzione. Con espressione intendo la "matematica" (o funzionale, di attenersi ai programmi) l'espressione di esso, per esempio: a un double xio associare il double x*x. In matematica lo scrivi usando il simbolo di latex "\ mapsto" . In C # notazione funzionale è stato preso in prestito: =>. Per esempio :

Func<double, double> thefunctor = ( (double x) => x * x ); // outer brackets are not
                                                           // mandatory

(double x) => x * xè un'espressione . Non è un tipo, mentre i delegati (generici o meno) lo sono.

Moralità ? Alla fine, cos'è un delegato (rispettivamente delegato generico), se non un tipo di puntatore a funzione (risp. Avvolto + smart + tipo di puntatore generico a funzione), eh? Qualcos'altro ! Vedi questo e quello .


-1

Bene, la versione davvero semplificata è che un lambda è solo una scorciatoia per una funzione anonima. Un delegato può fare molto di più che semplici funzioni anonime: cose come eventi, chiamate asincrone e più catene di metodi.


1
lambda possono essere usati come gestori di eventi; button.Click + = (sender, eventArgs) => {MessageBox.Show ("Click"); } e chiamato in modo asincrono new System.Threading.Thread (() => Console.Write ("Executed on a thread")). Start ();
Steve Cooper
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.