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


84

Diese Frage wird mir oft gestellt und ich dachte, ich würde um Input bitten, wie ich den Unterschied am besten beschreiben kann.


2
Mit "Delegierten" meinen Sie Delegiertentypen oder anonyme Delegierte? Sie sind auch unterschiedlich.
Chris Ammerman


1
Warum machen die Leute seine Frage so kompliziert? Beantworten Sie einfach, was ein Delegierter und was ein Lambda ist. Geben Sie so viele Erklärungen wie möglich und lassen Sie ihn wählen, was für ihn angemessen ist.
Imir Hoxha

Antworten:


95

Sie sind eigentlich zwei sehr unterschiedliche Dinge. "Delegieren" ist eigentlich der Name für eine Variable, die einen Verweis auf eine Methode oder ein Lambda enthält, und ein Lambda ist eine Methode ohne permanenten Namen.

Lambdas sind anderen Methoden sehr ähnlich, mit Ausnahme einiger subtiler Unterschiede.

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

Ein Delegat ist wie folgt definiert:

delegate Int32 BinaryIntOp(Int32 x, Int32 y);

Einer Variablen vom Typ BinaryIntOp kann entweder eine Methode oder ein Labmda zugewiesen werden, sofern die Signatur identisch ist: zwei Int32-Argumente und eine Int32-Rückgabe.

Ein Lambda könnte folgendermaßen definiert werden:

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

Eine andere zu beachtende Sache ist, dass die generischen Func- und Action-Typen oft als "Lambda-Typen" betrachtet werden, aber genau wie alle anderen Delegierten sind. Das Schöne an ihnen ist, dass sie im Wesentlichen einen Namen für jede Art von Delegat definieren, die Sie möglicherweise benötigen (bis zu 4 Parameter, obwohl Sie sicherlich mehr eigene hinzufügen können). Wenn Sie also eine Vielzahl von Delegatentypen verwenden, jedoch nicht mehr als einmal, können Sie mithilfe von Func und Action vermeiden, dass Ihr Code mit Delegatdeklarationen überladen wird.

Hier ist ein Beispiel dafür, 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 Information ist, dass Delegatentypen (nicht Methoden selbst) mit derselben Signatur, aber unterschiedlichen Namen nicht implizit ineinander umgewandelt werden. Dies schließt die Func- und Action-Delegierten ein. Wenn die Signatur jedoch identisch ist, können Sie explizit zwischen ihnen wechseln.

Die Extrameile gehen ... In C # sind Funktionen flexibel, mit der Verwendung von Lambdas und Delegierten. C # hat jedoch 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 Compilertrick. Wenn Sie eine Anweisung beginnen, indem Sie den Funktionsnamen gefolgt von einem Punkt schreiben (dh versuchen, einen Mitgliederzugriff auf die Funktion selbst durchzuführen), werden Sie feststellen, dass dort keine Mitglieder vorhanden sind, auf die verwiesen werden kann. Nicht einmal die von Object. Dies verhindert, dass der Programmierer nützliche (und natürlich möglicherweise 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 die den Unterschied zwischen anonymen Delegierten und Methoden und Lambdas veranschaulicht.

Update 2: James Hart macht einen wichtigen, wenn auch sehr technischen Hinweis darauf, dass Lambdas und Delegaten keine .NET-Entitäten sind (dh die CLR hat kein Konzept für einen Delegaten oder ein Lambda), sondern Framework- und Sprachkonstrukte.


Gute Erklärung. Obwohl ich denke, Sie meinen "erstklassige Funktionen", nicht "erstklassige Objekte". :)
ibz

1
Du hast recht. Ich hatte den Satz beim Schreiben anders aufgebaut ("C # -Funktionen sind eigentlich keine erstklassigen Objekte") und habe vergessen, das zu ändern. Vielen Dank!
Chris Ammerman

Eine normale Methode ist in einer "Anweisung" definiert. Eine Anweisung ist eine Aktion in der Reihenfolge eines imperativen Programms, möglicherweise basierend auf einem Ausdruck. Ist eine Methodendefinition nicht eine andere grammatikalische Struktur? Methodendefinition ist nicht in docs.microsoft.com/en-us/dotnet/csharp/tour-of-csharp/… aufgeführt
Max Barraclough

31

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

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

  • Was ist der Unterschied zwischen Lambda-Ausdrücken und anonymen Delegaten 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 etwas irgendwo zwischen oder um diese Extreme?

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 viel Sinn macht.

Das .NET Framework versteht an sich nicht die Konzepte anonymer Delegaten, Lambda-Ausdrücke oder Schließungen - all dies wird durch Sprachspezifikationen definiert. Überlegen Sie, wie der C # -Compiler die Definition einer anonymen Methode in eine Methode für eine generierte Klasse mit Mitgliedsvariablen übersetzt, um den Abschlussstatus beizubehalten. Für .NET ist der Delegat nicht anonym. Es ist nur anonym für den C # -Programmierer, der es schreibt. Dies gilt auch für einen Lambda-Ausdruck, der einem Delegatentyp zugewiesen ist.

Was .NET DOES verstehen , ist die Idee eines Delegierten - ein Typ, der eine Methodensignatur beschreibt, Instanzen , von denen entweder darstellen Anrufe auf bestimmte Methoden auf bestimmte Objekte gebunden oder ungebunden Anrufe an eine bestimmte Methode auf einen bestimmten Typ, der gegen aufgerufen werden kann jedes Objekt dieses Typs, bei dem das Verfahren der Signatur entspricht. Solche Typen erben alle von System.Delegate.

In .NET 3.5 wird außerdem der System.Linq.Expressions-Namespace eingeführt, der Klassen zur Beschreibung von Codeausdrücken enthält und 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, codegenniert 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 zum Erstellen des Ausdrucks zur Laufzeit erzeugt.

Natürlich, wenn Sie wurden gefragt , was der Unterschied zwischen Lambda - Ausdrücke und anonymen Methoden in C # ist, nach allem, dann ist das alles ziemlich irelevant, und in diesem Fall ist der Hauptunterschied der Kürze, der lehnt dich in Richtung anonym Delegierten , wenn Sie don‘ Sie kümmern sich nicht um Parameter und planen nicht, einen Wert zurückzugeben, und in Richtung Lambdas, wenn Sie abgeleitete Parameter und Rückgabetypen eingeben möchten.

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


3
Tolle Infos! Sie haben mich dazu inspiriert, den Reflektor anzuzünden und mir die IL anzusehen. Ich wusste nicht, dass Lambdas zu generierten Klassen führen, aber jetzt, wo ich darüber nachdenke, macht es vollkommen Sinn.
Chris Ammerman

20

Ein Unterschied besteht darin, dass ein anonymer Delegat Parameter weglassen kann, während ein Lambda mit der genauen Signatur übereinstimmen 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 hat):

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

13

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


Genau! Es gibt keinen Unterschied". Sie sind zwei von Natur aus verschiedene Dinge.
ibz

3

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


3

Ein Delegat ist im Grunde immer nur ein Funktionszeiger. Ein Lambda kann sich in einen Delegaten verwandeln, aber es kann sich auch in einen LINQ-Ausdrucksbaum verwandeln. 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.


2
Das stimmt, aber der Unterschied zwischen ihnen besteht darin, dass es sich um zwei völlig unterschiedliche Konzepte handelt . Das ist wie ein Vergleich von Äpfeln und Orangen. Siehe Dan Shields Antwort.
ibz

2

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

Ich glaube, das sind die gleichen:

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

2
Keines dieser Beispiele wird kompiliert. Auch wenn Sie den Namen der Instanz Delegatevon 'delegate' ändern , was ein Schlüsselwort ist.
Steve Cooper

2

Ein Delegat ist eine Funktionssignatur. etwas wie

delegate string MyDelegate(int param1);

Der Delegierte implementiert kein Gremium.

Das Lambda ist ein Funktionsaufruf, der der Signatur des Delegaten entspricht. Für den oben genannten Delegaten können Sie eine der folgenden Optionen verwenden:

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

Der DelegateTyp ist jedoch schlecht benannt; Durch das Erstellen eines Objekts vom Typ wird Delegatetatsächlich eine Variable erstellt, die Funktionen enthalten kann - seien es Lambdas, statische Methoden oder Klassenmethoden.


Wenn Sie eine Variable vom Typ MyDelegate erstellen, ist dies nicht wirklich der Laufzeittyp. Der Laufzeittyp ist Delegieren. Es gibt Compiler-Tricks, die beim Kompilieren von Delegaten, Lambdas und Ausdrucksbäumen eine Rolle spielen. Ich denke, der Code impliziert Dinge, die nicht wahr sind.
Chris Ammerman

2

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

Ein Lambda-Ausdruck ist eine Form der anonymen Funktion.


2

Ein Delegat ist eine Warteschlange mit Funktionszeigern. Beim Aufrufen eines Delegaten können mehrere Methoden aufgerufen werden. Ein Lambda ist im Wesentlichen eine anonyme Methodendeklaration, die vom Compiler je nach verwendetem Kontext 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 Delegatentyp erwartet, wird er vom Compiler für Sie umgewandelt. Wenn Sie es in einer LINQ-Anweisung verwenden, wird das Lambda vom Compiler in einen Ausdrucksbaum anstatt nur in einen Delegaten übersetzt.

Der Unterschied besteht darin, dass ein Lambda eine knappe Möglichkeit ist, eine Methode innerhalb eines anderen Ausdrucks zu definieren, während ein Delegat ein tatsächlicher Objekttyp ist.


2

Es ist ziemlich klar, dass die Frage lauten sollte: "Was ist der Unterschied zwischen Lambdas und anonymen Delegierten?" Von allen Antworten hier hat nur eine Person es richtig verstanden - der Hauptunterschied besteht darin, dass Lambdas sowohl zum Erstellen von Ausdrucksbäumen als auch von Delegierten verwendet werden können.

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


1

Delegierte sind wirklich nur strukturelle Typisierung für Funktionen. Sie können dasselbe mit der nominalen Eingabe und Implementierung einer anonymen Klasse tun, die eine Schnittstelle oder eine abstrakte Klasse implementiert. Dies ist jedoch eine Menge 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 Methode zum Erstellen von Funktionen. Sie werden besonders nützlich zum Erstellen von Funktionen

Während einige sagen könnten, Lambda sei syntaktischer Zucker für Delegierte, würde ich sagen, dass Delegierte eine Brücke sind, um Menschen in c # zu Lambdas zu bringen.


1

Einige grundlegende hier. "Delegieren" ist eigentlich der Name für eine Variable, die einen Verweis auf eine Methode oder ein Lambda enthält

Dies ist eine anonyme Methode -

(string 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. Zum Beispiel.

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

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

Gleiches gilt für den Lambda-Ausdruck. Normalerweise brauchen wir einen Delegierten, um sie zu benutzen

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

Wir können einen func-Delegaten verwenden, um diesen Ausdruck zu verwenden.

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

bool result = checkStudentAge ( Student Object);

0

Lambdas sind vereinfachte Versionen von Delegierten. Sie haben einige der Eigenschaften eines Abschlusses wie anonyme Delegaten, ermöglichen Ihnen jedoch auch die Verwendung der impliziten Eingabe. Ein Lambda wie dieses:

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

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

something.Sort(sortMethod);
...

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

Sie meinen, Lambdas sind wie vereinfachte anonyme Methoden (nicht delegieren). Wie Methoden (anonym oder nicht) können sie einer Delegatenvariablen zugewiesen werden.
Lucas

0

Hier ist ein Beispiel, das ich eine Weile in meinem lahmen Blog veröffentlicht habe. Angenommen, Sie möchten ein Etikett aus einem Arbeitsthread aktualisieren. Ich habe 4 Beispiele, wie dieses Label mithilfe von Delegaten, anon-Delegierten und 2 Arten von Lambdas von 1 auf 50 aktualisiert werden 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

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

Ein ( normaler , im Gegensatz zu sogenannten generischen Delegaten, siehe später) Delegat sollte als eine Art c ++ typedefeines Funktionszeigertyps angesehen werden, beispielsweise in c ++:

R (*thefunctionpointer) ( T ) ;

typedef ist der Typ, thefunctionpointerder der Zeigertyp auf eine Funktion ist, die ein Objekt vom Typ nimmt Tund ein Objekt vom Typ zurückgibt R. Sie würden es so verwenden:

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

wo thefunctionwäre eine Funktion eine Aufnahme Tund Rückkehr ein R.

In c # würden Sie für 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

wo thefunctionwäre eine Funktion eine Aufnahme Tund Rückkehr ein R. Dies gilt für Delegierte, sogenannte normale Delegierte.

Jetzt haben Sie auch generische Delegaten in c #, bei denen es sich um generische Delegaten handelt, dh die sozusagen "Vorlagen" sind, wobei ein c ++ - Ausdruck verwendet wird. Sie sind wie folgt definiert:

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

Und Sie können sie so 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);

wo thefunction2nimmt eine Funktion als Argument und eine Rückkehr double.

Stellen Sie sich nun vor, dass thefunction2ich stattdessen eine "Funktion" verwenden möchte, die im Moment nirgends durch eine Anweisung definiert ist und die ich später nie mehr verwenden werde. Dann erlaubt uns c #, den Ausdruck dieser Funktion zu verwenden. Mit dem Ausdruck meine ich die „mathematische“ (oder funktional, um Programme zu bleiben) Ausdruck davon, zum Beispiel: zu a double xIch werde assoziieren die double x*x. In der Mathematik schreiben Sie dies mit dem Latexsymbol "\ mapsto" . 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 * xist ein Ausdruck . Es ist kein Typ, wohingegen Delegierte (generisch oder nicht) sind.

Moral? Was ist am Ende ein Delegat (bzw. generischer Delegat), wenn nicht ein Funktionszeigertyp (bzw. verpackter + intelligenter + generischer Funktionszeigertyp)? Etwas anderes ! Sehen Sie dies und das .


-1

Nun, die wirklich vereinfachte Version ist, dass ein Lambda nur eine Abkürzung für eine anonyme Funktion ist. Ein Delegat kann viel mehr als nur anonyme Funktionen ausführen: Ereignisse, asynchrone Aufrufe und mehrere Methodenketten.


1
Lambdas können als Ereignishandler verwendet werden. button.Click + = (Absender, eventArgs) => {MessageBox.Show ("Click"); } und asynchron aufgerufen new System.Threading.Thread (() => Console.Write ("Auf einem Thread ausgeführt")). Start ();
Steve Cooper
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.