it-swarm-eu.dev

Was ist der Unterschied zwischen Lambdas und Delegaten in .NET Framework?

Diese Frage wird mir häufig gestellt, und ich dachte, ich würde Anregungen geben, wie ich den Unterschied am besten beschreiben kann.

66
ScottKoon

Sie sind eigentlich zwei sehr verschiedene Dinge. "Delegate" ist eigentlich der Name einer Variablen, die einen Verweis auf eine Methode oder ein Lambda enthält, und ein Lambda ist eine Methode ohne festen Namen.

Lambdas sind anderen Methoden sehr ähnlich, abgesehen von ein paar subtilen Unterschieden.

  1. Eine normale Methode ist in einer "Anweisung" definiert und an einen permanenten Namen gebunden, während ein Lambda "on the fly" in einem "Ausdruck" definiert ist und keinen permanenten Namen hat.
  2. Einige Lambdas können mit .NET-Ausdrucksbäumen verwendet werden, Methoden dagegen nicht.

Ein Delegierter ist folgendermaßen definiert:

delegate Int32 BinaryIntOp(Int32 x, Int32 y);

Einer Variablen des Typs BinaryIntOp kann entweder eine Methode oder ein Labmda zugewiesen werden, solange die Signatur identisch ist: zwei Int32-Argumente und ein Int32-Return.

Ein Lambda könnte folgendermaßen definiert werden:

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

Zu beachten ist auch, dass die generischen Func- und Action-Typen zwar oft als "Lambda-Typen" betrachtet werden, sie sind jedoch genauso wie alle anderen Delegierten. Das Schöne an ihnen ist, dass sie im Wesentlichen einen Namen für alle Arten von Delegierten definieren, die Sie benötigen (bis zu 4 Parameter, Sie können jedoch sicherlich noch weitere hinzufügen). Wenn Sie also eine Vielzahl von Delegattypen verwenden, jedoch nicht mehr als einmal, können Sie mithilfe von Func und Action vermeiden, dass Ihr Code mit Delegatdeklarationen überladen wird.

Hier ist eine Illustration, wie Func und Action "nicht nur für Lambdas" sind:

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

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

Eine weitere nützliche Sache ist, dass Delegat-Typen (nicht die Methoden selbst) mit derselben Signatur, aber unterschiedlichen Namen, nicht implizit ineinander gegliedert werden. Dies schließt die Delegierten von Func und Action ein. Wenn die Signatur jedoch identisch ist, können Sie sie explizit umwandeln.

Die Extra-Meile gehen ... In C # sind Funktionen flexibel, mit Lambdas und Delegierten. Aber C # hat keine "erstklassigen Funktionen". Sie können den einer Delegatenvariablen zugewiesenen Funktionsnamen verwenden, um im Wesentlichen ein Objekt zu erstellen, das diese Funktion darstellt. Aber es ist wirklich ein Compiler-Trick. Wenn Sie eine Anweisung starten, indem Sie den Funktionsnamen gefolgt von einem Punkt schreiben (d. H., Versuchen Sie, den Member-Zugriff auf die Funktion selbst auszuführen), werden Sie feststellen, dass dort keine Member vorhanden sind. Nicht einmal die von Object. Dadurch wird verhindert, dass der Programmierer nützliche (und natürlich potenziell gefährliche) Dinge ausführt, z. B. das Hinzufügen von Erweiterungsmethoden, die für jede Funktion aufgerufen werden können. Das Beste, was Sie tun können, ist, die Delegate-Klasse selbst zu erweitern, was sicherlich auch nützlich ist, aber nicht ganz so viel.

Update: Siehe auch Kargs Antwort illustriere den Unterschied zwischen anonymen Delegierten vs. Methoden & Lambdas.

Update 2: James Hart macht einen wichtigen, wenn auch sehr technischen Hinweis, dass Lambdas und Delegaten keine .NET-Entitäten sind (d. H. Die CLR hat kein Konzept eines Delegaten oder Lambda), sondern eher Rahmen- und Sprachkonstrukte.

79
Chris Ammerman

Die Frage ist etwas mehrdeutig, was die große Ungleichheit der Antworten erklärt, die Sie erhalten.

Sie haben tatsächlich gefragt, was der Unterschied zwischen Lambdas und Delegierten im .NET-Framework ist. Das könnte eines von vielen Dingen sein. Fragst du:

  • Was ist der Unterschied zwischen Lambda-Ausdrücken und anonymen Delegierten in der Sprache C # (oder VB.NET)?

  • Was ist der Unterschied zwischen System.Linq.Expressions.LambdaExpression-Objekten und System.Delegate-Objekten in .NET 3.5?

  • Oder irgendetwas irgendwo zwischen oder um diese Extreme herum?

Einige Leute scheinen zu versuchen, Ihnen die Antwort auf die Frage 'Was ist der Unterschied zwischen C # -Lambda-Ausdrücken und .NET System.Delegate?' Zu geben, was nicht sehr viel Sinn ergibt.

Das .NET-Framework versteht an sich nicht die Konzepte anonymer Delegaten, Lambda-Ausdrücke oder Schließungen. Dies sind alles Dinge, die durch Sprachspezifikationen definiert werden. Stellen Sie sich vor, wie der C # -Compiler die Definition einer anonymen Methode in eine Methode für eine generierte Klasse umwandelt, deren Member-Variablen den Abschlussstatus enthalten. In .NET gibt es nichts Anonymes über den Delegierten. Es ist nur anonym, wenn der C # -Programmierer es schreibt. Dies gilt auch für einen Lambda-Ausdruck, der einem Delegatentyp zugewiesen ist.

Was .NETTUTunderstand ist die Idee eines Delegaten - ein Typ, der eine Methodensignatur beschreibt, deren Instanzen entweder gebundene Aufrufe bestimmter Methoden für bestimmte Objekte oder ungebundene Aufrufe einer bestimmten Methode darstellen ein bestimmter Typ, der für ein beliebiges Objekt dieses Typs aufgerufen werden kann, wenn die Methode der Signatur entspricht. Solche Typen erben alle von System.Delegate.

.NET 3.5 führt außerdem den System.Linq.Expressions-Namespace ein, der Klassen zur Beschreibung von Codeausdrücken enthält und der daher auch gebundene oder ungebundene Aufrufe von Methoden für bestimmte Typen oder Objekte darstellen kann. LambdaExpression-Instanzen können dann zu tatsächlichen Delegaten kompiliert werden (wobei eine dynamische Methode, die auf der Struktur des Ausdrucks basiert, codiert wird und ein Delegatenzeiger darauf zurückgegeben wird).

In C # können Sie Instanzen von System.Expressions.Expression-Typen erstellen, indem Sie einer Variablen dieses Typs einen Lambda-Ausdruck zuweisen, der den entsprechenden Code zur Erstellung des Ausdrucks zur Laufzeit erzeugt.

Wenn Sie waren fragen, was der Unterschied zwischen Lambda-Ausdrücken und anonymen Methoden in C # ist, ist dies alles ziemlich irrelevant, und in diesem Fall ist der Hauptunterschied die Abkürzung, die dazu neigt Anonyme Delegierte, wenn Sie sich nicht für Parameter interessieren und nicht vorhaben, einen Wert zurückzugeben, und in Richtung Lambdas, wenn Sie vom Typ abgeleitete Parameter und Rückgabetypen verwenden möchten.

Und Lambda-Ausdrücke unterstützen die Ausdrucksgenerierung.

27
James Hart

Ein Unterschied besteht darin, dass ein anonymer Delegat Parameter auslassen kann, während ein Lambda der genauen Signatur entsprechen muss. Gegeben:

public delegate string TestDelegate(int i);

public void Test(TestDelegate d)
{}

sie können es auf die folgenden vier Arten aufrufen (beachten Sie, dass die zweite Zeile einen anonymen Delegaten hat, der keine Parameter enthält):

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

Sie können keinen Lambda-Ausdruck ohne Parameter oder eine Methode ohne Parameter übergeben. Diese sind nicht erlaubt:

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

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

Delegaten entsprechen Funktionszeigern/Methodenzeigern/Rückrufen (treffen Sie Ihre Wahl), und Lambdas sind ziemlich vereinfachte anonyme Funktionen. Zumindest erzähle ich es den Leuten.

13
Dan Shield

Ich habe nicht viel Erfahrung damit, aber ich würde es so beschreiben, dass ein Delegat ein Wrapper für jede Funktion ist, während ein Lambda-Ausdruck selbst eine anonyme Funktion ist.

3
chessguy

Ein Delegat ist immer nur ein Funktionszeiger. Aus einem Lambda kann ein Delegat werden, aber es kann auch ein LINQ-Ausdrucksbaum werden. Zum Beispiel,

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

Die erste Zeile erzeugt einen Delegaten, während die zweite einen Ausdrucksbaum erzeugt.

3

Ein Delegat ist eine Referenz auf eine Methode mit einer bestimmten Parameterliste und einem Rückgabetyp. Es kann ein Objekt enthalten oder nicht.

Ein Lambda-Ausdruck ist eine Form einer anonymen Funktion.

2
Peter Ritchie

lambdas sind einfach syntaktischer Zucker für einen Delegierten. Der Compiler konvertiert schließlich Lambdas in Delegierte.

Das sind die gleichen, glaube ich:

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

Ein Delegat ist eine Funktionssignatur. so etwas wie 

delegate string MyDelegate(int param1);

Der Delegierte führt keine Einrichtung ein. 

Das Lambda ist ein Funktionsaufruf, der der Signatur des Delegierten entspricht. Für den obigen Delegierten können Sie Folgendes verwenden:

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

Der Delegate-Typ hat jedoch einen falschen Namen. Wenn Sie ein Objekt vom Typ Delegate erstellen, wird tatsächlich eine Variable erstellt, die Funktionen enthalten kann, z. B. Lambdas, statische Methoden oder Klassenmethoden.

2
Steve Cooper

Es ist ziemlich klar, dass die Frage lautete "Was ist der Unterschied zwischen Lambdas und anonymous delegates?" Von allen Antworten hier hat nur eine Person es richtig verstanden - der Hauptunterschied besteht darin, dass mit Lambdas sowohl Ausdrucksbäume als auch Delegierte erstellt werden können.

Weitere Informationen finden Sie unter MSDN: http://msdn.Microsoft.com/en-us/library/bb397687.aspx

2
Philip Beber

Ein Delegat ist eine Warteschlange von Funktionszeigern. Beim Aufrufen eines Delegaten können mehrere Methoden aufgerufen werden. Ein Lambda ist im Wesentlichen eine anonyme Methodendeklaration, die vom Compiler je nach Kontext als unterschiedlich interpretiert werden kann.

Sie können einen Delegaten erhalten, der auf den Lambda-Ausdruck als Methode verweist, indem Sie ihn in einen Delegaten umwandeln. Wenn Sie ihn als Parameter an eine Methode übergeben, die einen bestimmten Delegattyp erwartet, wird der Compiler ihn für Sie umsetzen. Wird der Lambda innerhalb einer LINQ-Anweisung verwendet, wird der Lambda vom Compiler in einen Ausdrucksbaum und nicht nur in einen Delegaten übersetzt.

Der Unterschied besteht wirklich darin, dass ein Lambda eine knappe Methode zum Definieren einer Methode innerhalb eines anderen Ausdrucks ist, während ein Delegat ein tatsächlicher Objekttyp ist.

1
justin.m.chase

Delegierte sind wirklich nur strukturelle Typisierung für Funktionen. Sie können dasselbe mit der nominalen Typisierung tun und eine anonyme Klasse implementieren, die eine Schnittstelle oder eine abstrakte Klasse implementiert. Dies bedeutet jedoch viel Code, wenn nur eine Funktion benötigt wird.

Lambda stammt aus der Idee des Lambda-Kalküls der Alonzo-Kirche in den 1930er Jahren. Es ist eine anonyme Art, Funktionen zu erstellen. Sie werden besonders nützlich beim Zusammenstellen von Funktionen

Während einige sagen, Lambda ist syntaktischer Zucker für Delegierte, würde ich sagen, Delegierte sind eine Brücke, um Menschen in c # in Lambdas zu lockern.

1
Steve g

Ich gehe davon aus, dass Ihre Frage c # und nicht .NET betrifft, wegen der Mehrdeutigkeit Ihrer Frage, da .NET nicht alleine - dh ohne c # - Verständnis von Delegaten und Lambda-Ausdrücken - kommt.

Ein ( normal , im Gegensatz zu sogenannten generischen delegates, cf later) Delegaten sollte als eine Art c ++ typedef eines Funktionszeigertyps betrachtet werden, z Instanz in C++:

R (*thefunctionpointer) ( T ) ;

typedef ist der Typ thefunctionpointer. Dies ist der Typ von Zeigern auf eine Funktion, die ein Objekt vom Typ T nimmt und ein Objekt vom Typ R zurückgibt. Sie würden es so verwenden:

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

dabei wäre thefunction eine Funktion, die eine T und eine R zurückgibt.

In c # würden Sie gehen

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

und du würdest es so benutzen:

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

dabei wäre thefunction eine Funktion, die eine T und eine R zurückgibt. Dies ist für Delegierte, so genannte normale Delegierte.

Nun gibt es auch generische Delegaten in c #, die generische Delegaten sind, also d. H. die sozusagen "templiert" werden und dabei einen c ++ - Ausdruck verwenden. Sie sind wie folgt definiert:

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

Und Sie können sie wie folgt verwenden:

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

dabei ist thefunction2 eine Funktion, die als Argument eine double zurückgibt.

Stellen Sie sich nun vor, dass ich anstelle von thefunction2 eine "Funktion" verwenden möchte, die vorerst nirgendwo durch eine Anweisung definiert ist und die ich später niemals verwenden werde. Dann können wir mit c # expression dieser Funktion verwenden. Mit Ausdruck meine ich den Ausdruck "mathematisch" (oder funktional, um sich an Programme zu halten), zum Beispiel: an einen double x ich werde associate die doublex*x. In der Mathematik schreiben Sie dies mit dem "\ mapsto" -Latex-Symbol . In c # wurde die funktionale Notation ausgeliehen: =>. Zum Beispiel :

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

(double x) => x * x ist ein Ausdruck . Es ist kein Typ, wohingegen Delegierte (generisch oder nicht) sind.

Moral? Was ist am Ende ein Delegat (bzw. ein generischer Delegat), wenn nicht ein Funktionszeigertyp (bzw. wrapped + smart + generischer Funktionszeigertyp), oder? Etwas anderes ! Siehe das und das .

Hier ist ein Beispiel, das ich eine Weile in meinem lahmen Blog veröffentlicht habe. Angenommen, Sie wollten ein Etikett aus einem Arbeitsthread aktualisieren. Ich habe 4 Beispiele dafür, wie man dieses Label von 1 bis 50 mit Delegierten, 1 Delegaten und 2 Arten von Lambdas aktualisieren kann.

 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
Echostorm

Einige grundlegende hier. "Delegate" ist eigentlich der Name einer Variablen, die einen Verweis auf eine Methode oder ein Lambda enthält

Dies ist eine anonyme Methode - (Zeichenfolge testString) => {Console.WriteLine (testString); };

Da anonyme Methoden keinen Namen haben, benötigen wir einen Delegaten, in dem wir beide Methoden oder Ausdrücke zuweisen können. Für Bsp.

delegate void PrintTestString (Zeichenfolge testString); // einen Delegierten deklarieren

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


Gleiches gilt für den Lambda-Ausdruck. Normalerweise brauchen wir Delegierte, um sie zu nutzen

s => s.Age> someValue && s.Age <someValue // gibt true/false zurück

Wir können einen Funktionsdelegierten verwenden, um diesen Ausdruck zu verwenden.

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

bool result = checkStudentAge (Schülerobjekt);

0

Lambdas sind vereinfachte Versionen von Delegierten. Sie haben einige der Eigenschaften eines Closure wie anonyme Delegaten, erlauben aber auch die Verwendung von impliziten Typisierungen. Ein Lambda wie dieses:

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

ist viel prägnanter als das, was Sie mit einem Delegierten machen können:

something.Sort(sortMethod);
...

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