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

StackOverflow https://stackoverflow.com/questions/73227

  •  09-06-2019
  •  | 
  •  

Question

On me pose souvent cette question et je pensais avoir des idées pour décrire au mieux la différence.

Était-ce utile?

La solution

Ce sont en fait deux choses très différentes. " Délégué " 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 une " instruction " et est liée à un nom permanent, alors qu'un lambda est défini "à la volée" dans une "expression" & ; elle n'a pas de nom permanent.
  2. Certains lambdas peuvent être utilisés avec des arbres d'expression .NET, alors que les méthodes ne le peuvent pas.

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

delegate Int32 BinaryIntOp(Int32 x, Int32 y);

Une variable de type BinaryIntOp peut avoir une méthode ou une labmda attribuée, tant que la signature est identique: deux arguments Int32 et un retour Int32.

Un lambda peut ê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 sont "non 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 transtypés entre eux. Cela inclut les délégués Func et Action. Toutefois, si la signature est identique, vous pouvez explicitement les transtyper.

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, essayez de faire 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 tout à fait autant.

Mise à jour: voir également La réponse de Karg illustrant la différence entre les délégués anonymes et les méthodes & amp; lambdas.

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

Autres conseils

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 les lambdas et les délégués dans le framework .NET; cela pourrait être l'une des nombreuses choses. Demandez-vous:

  • 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 lambda C # 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 - c’est tout ce qui est défini 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. C'est également vrai d'une expression lambda assignée à un type de délégué.

Qu'est-ce que .NET FAIT , c'est l'idée d'un délégué - un type qui décrit une signature de méthode, dont les instances représentent des appels liés à des méthodes spécifiques sur des objets spécifiques, ou des appels non liés à un utilisateur particulier procédé sur un type particulier qui peut être invoqué contre n'importe quel objet de ce type, où ledit procédé adhère à ladite signature. De tels types héritent tous de System.Delegate.

.NET 3.5 introduit également l’espace de noms System.Linq.Expressions, qui contient des classes permettant de décrire des 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 objets particuliers. Les occurrences LambdaExpression peuvent ensuite être compilées en 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 #, alors tout cela n’est guère pertinent et dans ce cas, la principale différence est la brièveté. , qui s’adresse aux délégués anonymes lorsque vous ne vous souciez pas des paramètres et ne prévoyez pas de retourner une valeur, et aux lambdas lorsque vous souhaitez taper des paramètres et des types renvoyés.

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

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

public delegate string TestDelegate(int i);

public void Test(TestDelegate d)
{}

vous pouvez l'appeler des quatre manières suivantes (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ètre ni une méthode sans paramètre. Ceux-ci 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;
}

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

Je n'ai pas beaucoup d'expérience avec cela, mais je le décrirais comme si un délégué enveloppait n'importe quelle fonction, alors qu'une expression lambda était elle-même une fonction anonyme.

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 génère un délégué, tandis que la seconde génère un arbre d'expression.

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

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 l'un des;

(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.

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.

Il est assez clair que la question était censée être "quelle est la différence entre lambdas et les délégués anonymes ?" Parmi toutes les réponses données ici, une seule personne a eu raison - 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

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 la composition de fonctions

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 #.

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 elle est utilisée.

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, c’est qu’un lambda est un moyen fastidieux de définir une méthode à l’intérieur d’une autre expression, alors qu’un délégué est un type d’objet réel.

Les Lambdas sont des versions simplifiées des délégués. Ils ont certaines des propriétés d’un fermeture comme des délégués anonymes, mais permettent également vous utilisez taper implicitement. 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)
}

Voici un exemple que j’ai mis de temps en 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 lambdas.

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

Je suppose que votre question concerne c # et non .NET, en raison de l'ambiguïté de votre question, car .NET ne se fait 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 aux soi-disant délégués génériques , cf plus tard) doit être considéré comme une sorte de code c ++ < > typedef d'un type de pointeur de fonction, par exemple en c ++:

R (*thefunctionpointer) ( T ) ;

typedef est le 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

la fonction serait une fonction prenant un T et renvoyant un R .

En c #, vous choisiriez

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

la fonction serait une fonction prenant un T et renvoyant un R . C’est pour les délégués, appelés délégués normaux.

Vous avez également des délégués génériques dans c #, qui sont des délégués génériques, c.-à-d. qui sont "modélisés". pour ainsi dire, en utilisant de ce fait une expression c ++. Ils sont définis comme suit:

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 un double .

Maintenant, imaginez qu'au lieu de thefunction2 , j'aimerais utiliser une "fonction". cela n'est nulle part défini pour le moment, par une déclaration, et que je n'utiliserai jamais plus tard. Ensuite, c # nous permet d’utiliser l’expression de de cette fonction. Par expression, je veux dire le "mathématique" (ou fonctionnelle, pour coller aux programmes) son expression, par exemple: à un double x je vais associer le double x * x . En maths, vous écrivez cela en utilisant le " \ \ mapsto " symbole 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 une 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 this et que .

Quelques bases ici. " Délégué " 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 attribuer ces deux méthodes ou expressions. Par exemple.

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

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

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

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

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 ( Student Object);

Eh bien, la version vraiment simpliste est qu’un lambda est un raccourci pour une fonction anonyme. Un délégué peut faire beaucoup plus que des fonctions anonymes: des événements tels que des événements, des appels asynchrones et plusieurs chaînes de méthodes.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top