web-dev-qa-db-fra.com

Quelle est la différence entre lambdas et les délégués dans le .NET Framework?

On me pose souvent cette question et je pensais avoir des idées sur la meilleure façon de décrire la différence.

66
ScottKoon

Ce sont en fait deux choses très différentes. "Delegate" est en fait le nom d'une variable qui contient une référence à une méthode ou à un lambda, et un lambda est une méthode sans nom permanent.

Les lambda ressemblent beaucoup aux autres méthodes, à quelques différences près.

  1. Une méthode normale est définie dans un "statement" et est liée à un nom permanent, tandis qu'un lambda est défini "à la volée" dans un "expression" et n'a pas de nom permanent.
  2. Certains lambdas peuvent être utilisés avec des arbres d'expression .NET, contrairement aux méthodes.

Un délégué est défini comme ceci:

delegate Int32 BinaryIntOp(Int32 x, Int32 y);

Une variable de type BinaryIntOp peut être associée à une méthode ou à une labmda, à condition que la signature soit la même: deux arguments Int32 et un retour Int32.

Un lambda pourrait être défini comme ceci:

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

Une autre chose à noter est que, bien que les types génériques Func et Action soient souvent considérés comme des "types lambda", ils sont comme d’autres délégués. La bonne chose à leur sujet est qu’ils définissent essentiellement un nom pour tout type de délégué dont vous pourriez avoir besoin (jusqu’à 4 paramètres, bien que vous puissiez certainement en ajouter d’autres). Ainsi, si vous utilisez une grande variété de types de délégués, mais pas plus d'une fois, vous pouvez éviter d'encombrer votre code avec des déclarations de délégués en utilisant Func et Action.

Voici une illustration de la façon dont Func et Action ne sont "pas seulement pour les lambdas":

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

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

Une autre chose utile à savoir est que les types de délégué (pas les méthodes elles-mêmes) avec la même signature mais des noms différents ne seront pas implicitement attribués les uns aux autres. Cela inclut les délégués Func et Action. Cependant, si la signature est identique, vous pouvez explicitement transtyper entre elles.

Aller plus loin… En C #, les fonctions sont flexibles, avec l'utilisation de lambdas et de délégués. Mais C # n'a pas de "fonctions de première classe". Vous pouvez utiliser le nom d'une fonction attribué à une variable déléguée pour créer essentiellement un objet représentant cette fonction. Mais c'est vraiment un truc de compilateur. Si vous commencez une déclaration en écrivant le nom de la fonction suivi d’un point (c’est-à-dire que vous essayez de créer un accès membre sur la fonction elle-même), vous constaterez qu’il n’y a aucun membre à référencer. Pas même ceux d'Object. Cela empêche le programmeur de faire des choses utiles (et potentiellement dangereuses bien sûr) telles que l’ajout de méthodes d’extension pouvant être appelées par n’importe quelle fonction. Le mieux que vous puissiez faire est d’étendre la classe Delegate elle-même, ce qui est sûrement également utile, mais pas autant.

Mise à jour: Voir aussi Réponse de Karg illustrant la différence entre les délégués anonymes et les méthodes & lambdas.

Mise à jour 2: James Hart fait une remarque importante, bien que très technique, que les lambdas et les délégués ne sont pas des entités .NET (c’est-à-dire que le CLR n’a pas de concept de délégué ou de lambda), mais plutôt des constructions de cadre et de langage.

79
Chris Ammerman

La question est un peu ambiguë, ce qui explique la grande disparité des réponses que vous obtenez.

Vous avez en fait demandé quelle était la différence entre lambdas et les délégués dans le framework .NET; cela pourrait être l'une des nombreuses choses. Vous demandez:

  • Quelle est la différence entre les expressions lambda et les délégués anonymes en langage C # (ou VB.NET)?

  • Quelle est la différence entre les objets System.Linq.Expressions.LambdaExpression et les objets System.Delegate dans .NET 3.5?

  • Ou quelque chose entre ou autour de ces extrêmes?

Certaines personnes semblent essayer de vous donner la réponse à la question «Quelle est la différence entre les expressions C # Lambda et .NET System.Delegate?», Ce qui n'a pas beaucoup de sens.

Le framework .NET ne comprend pas en soi les concepts de délégué anonyme, d’expression lambda ou de fermeture - ce sont toutes des choses définies par les spécifications du langage. Réfléchissez à la façon dont le compilateur C # traduit la définition d'une méthode anonyme en une méthode sur une classe générée avec des variables membres pour conserver l'état de fermeture; Pour .NET, le délégué n'a rien d'anonyme; c'est juste anonyme pour le programmeur C # qui l'écrit. Cela est également vrai d'une expression lambda attribuée à un type de délégué.

Ce que .NET _ ne comprend pas bien est l’idée d’un délégué - un type qui décrit une signature de méthode, dont les instances représentent soit des appels liés à des méthodes spécifiques sur des objets spécifiques, soit des appels non liés à une méthode particulière sur un système particulier. type qui peut être invoqué contre n’importe quel objet de ce type, ladite méthode étant conforme à ladite signature. Ces types héritent tous de System.Delegate.

.NET 3.5 introduit également l’espace de noms System.Linq.Expressions, qui contient des classes pour décrire les expressions de code, et qui peut donc également représenter des appels liés ou non liés à des méthodes sur des types ou des objets particuliers. Les occurrences LambdaExpression peuvent ensuite être compilées dans des délégués réels (grâce à quoi une méthode dynamique, basée sur la structure de l'expression, est codée et où un pointeur de délégué est renvoyé).

En C #, vous pouvez produire des instances de types System.Expressions.Expression en affectant une expression lambda à une variable de ce type, ce qui produira le code approprié pour construire l'expression au moment de l'exécution.

Bien sûr, si vous demandez quelle est la différence entre les expressions lambda et les méthodes anonymes en C #, après tout, alors tout cela est assez peu pertinent, et dans ce cas, la principale différence est la brièveté, qui penche vers les délégués anonymes lorsque vous ne vous souciez pas des paramètres et que vous ne prévoyez pas de renvoyer une valeur, et vers lambdas lorsque vous souhaitez taper des paramètres inférés et des types retournés.

Et les expressions lambda prennent en charge la génération d’expression.

27
James Hart

Une différence est qu'un délégué anonyme peut omettre des paramètres alors qu'un lambda doit correspondre à la signature exacte. Donné:

public delegate string TestDelegate(int i);

public void Test(TestDelegate d)
{}

vous pouvez l'appeler de quatre manières (notez que la deuxième ligne a un délégué anonyme qui n'a aucun paramètre):

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

Vous ne pouvez pas transmettre une expression lambda sans paramètres ni une méthode sans paramètres. Ce ne sont pas autorisés:

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

Les délégués sont équivalents aux pointeurs de fonction/pointeurs de méthode/rappels (faites votre choix), et les lambdas sont des fonctions anonymes simplifiées. Au moins c'est ce que je dis aux gens.

13
Dan Shield

Je n'ai pas beaucoup d'expérience avec cela, mais je dirais qu'un délégué est un wrapper autour de n'importe quelle fonction, alors qu'une expression lambda est elle-même une fonction anonyme.

3
chessguy

Un délégué est toujours fondamentalement un pointeur de fonction. Un lambda peut devenir un délégué, mais il peut également se transformer en un arbre d'expression LINQ. Par exemple,

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

La première ligne produit un délégué, tandis que la seconde produit un arbre d'expression.

3
Curt Hagenlocher

Un délégué est une référence à une méthode avec une liste de paramètres et un type de retour particuliers. Il peut inclure ou non un objet.

Une expression lambda est une forme de fonction anonyme.

2
Peter Ritchie

les lambdas sont simplement du sucre syntaxique sur un délégué. Le compilateur finit par convertir les lambdas en délégués.

Ce sont les mêmes, je crois:

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

Un délégué est une signature de fonction. quelque chose comme 

delegate string MyDelegate(int param1);

Le délégué n'implémente pas de corps. 

Le lambda est un appel de fonction qui correspond à la signature du délégué. Pour le délégué ci-dessus, vous pouvez utiliser n'importe lequel de;

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

Le type Delegate est mal nommé, cependant; la création d'un objet de type Delegate crée en fait une variable pouvant contenir des fonctions, qu'il s'agisse de lambdas, de méthodes statiques ou de méthodes de classe.

2
Steve Cooper

Il est assez clair que la question était censée être "quelle est la différence entre les délégués lambdas et anonymes?" Parmi toutes les réponses données ici, une seule personne a bien compris - la principale différence est que lambdas peut être utilisé pour créer des arbres d’expression ainsi que des délégués.

Vous pouvez en savoir plus sur MSDN: http://msdn.Microsoft.com/en-us/library/bb397687.aspx

2
Philip Beber

Un délégué est une file d'attente de pointeurs de fonctions, l'appel d'un délégué peut invoquer plusieurs méthodes. Un lambda est essentiellement une déclaration de méthode anonyme qui peut être interprétée différemment par le compilateur, en fonction du contexte dans lequel il est utilisé.

Vous pouvez obtenir un délégué qui pointe l'expression lambda en tant que méthode en le transformant en délégué, ou si vous le transmettez en tant que paramètre à une méthode qui attend un type de délégué spécifique que le compilateur transmettra pour vous. En l'utilisant à l'intérieur d'une instruction LINQ, le lambda sera traduit par le compilateur en un arbre d'expression au lieu d'un simple délégué.

La différence est qu’un lambda est un moyen fastidieux de définir une méthode dans une autre expression, alors qu’un délégué est un type d’objet réel.

1
justin.m.chase

Les délégués ne sont en réalité que du typage structurel pour les fonctions. Vous pouvez faire la même chose avec le typage nominal et l'implémentation d'une classe anonyme implémentant une interface ou une classe abstraite, mais cela génère beaucoup de code lorsqu'une seule fonction est nécessaire.

Lambda vient de l’idée du lambda calcul de l’église Alonzo dans les années 1930. C'est une manière anonyme de créer des fonctions. Ils deviennent particulièrement utiles pour composer des fonctions

Ainsi, alors que certains pourraient dire que lambda est un sucre syntaxique pour les délégués, je dirais que les délégués sont un pont pour amener les gens à devenir lambdas en c #.

1
Steve g

Je suppose que votre question concerne c # et non .NET, en raison de l'ambiguïté de votre question, car .NET ne devient pas seul - c'est-à-dire sans c # - compréhension des délégués et des expressions lambda.

Un délégué (normal, par opposition à ce que l'on appelle les délégués generic, cf plus tard) doit être considéré comme une sorte de c ++ typedef de type pointeur de fonction, par exemple en c ++:

R (*thefunctionpointer) ( T ) ;

typedef est du type thefunctionpointer qui est le type des pointeurs sur une fonction prenant un objet de type T et renvoyant un objet de type R. Vous l'utiliseriez comme ceci:

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

thefunction serait une fonction prenant une T et retournant une R.

En c # vous iriez pour

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

et vous l'utiliseriez comme ceci:

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

thefunction serait une fonction prenant une T et retournant une R. Ceci est destiné aux délégués, appelés délégués ordinaires.

Maintenant, vous avez également des délégués génériques dans c #, qui sont des délégués génériques, i.e. qui sont "modélisés" pour ainsi dire, utilisant ainsi une expression c ++. Ils sont définis comme ceci:

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

Et vous pouvez les utiliser comme ceci:

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

thefunction2 est une fonction prenant comme argument et renvoyant une double.

Maintenant, imaginez qu'au lieu de thefunction2, j'aimerais utiliser une "fonction" qui n'est définie nulle part pour le moment, par une instruction, et que je n'utiliserai jamais plus tard. Ensuite, c # nous permet d’utiliser le expression de cette fonction. Par expression, j'entends l'expression "mathématique" (ou fonctionnelle, à coller aux programmes), par exemple: à un double x je vais associer la doublex*x. En maths, vous écrivez ceci en utilisant le symbole "\ mapsto" en latex . En c #, la notation fonctionnelle a été empruntée: =>. Par exemple :

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

(double x) => x * x est un expression . Ce n'est pas un type, alors que les délégués (génériques ou non) le sont.

Moralité ? À la fin, qu'est-ce qu'un délégué (resp. Générique), sinon un type de pointeur de fonction (resp. Enveloppé + smart + type de pointeur de fonction générique), hein? Autre chose ! Voir ceci et que .

Voici un exemple que j'ai donné un peu de temps sur mon blog boiteux. Supposons que vous souhaitiez mettre à jour une étiquette à partir d'un thread de travail. J'ai 4 exemples de mise à jour de cette étiquette de 1 à 50 à l'aide de délégués, de délégués anon et de 2 types de 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
Echostorm

Quelques bases ici . "Delegate" est en fait le nom d’une variable qui contient une référence à une méthode ou à un lambda

Ceci est une méthode anonyme - (String testString) => {Console.WriteLine (testString); };

Comme les méthodes anonymes n'ont pas de nom, nous avons besoin d'un délégué dans lequel nous pouvons affecter ces deux méthodes ou expressions. Pour ex.

delegate void PrintTestString (string testString); // déclarer un délégué

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


Idem avec l'expression lambda. Habituellement, nous avons besoin d'un délégué pour les utiliser

s => s.Age> someValue && s.Age <someValue // retournera vrai/faux

Nous pouvons utiliser un délégué func pour utiliser cette expression.

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

bool result = checkStudentAge (objet étudiant);

0
Yogesh Prajapati

Les Lambda sont des versions simplifiées des délégués. Ils ont certaines des propriétés d'un fermeture comme les délégués anonymes, mais vous permettent également d'utiliser le typage implicite. Un lambda comme ça:

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

est beaucoup plus concis que ce que vous pouvez faire avec un délégué:

something.Sort(sortMethod);
...

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