On me pose souvent cette question et j'ai pensé solliciter des commentaires sur la meilleure façon de décrire la différence.
On me pose souvent cette question et j'ai pensé solliciter des commentaires sur la meilleure façon de décrire la différence.
Réponses:
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 lambdas ressemblent beaucoup aux autres méthodes, à l'exception de quelques différences subtiles.
Un délégué est défini comme ceci:
delegate Int32 BinaryIntOp(Int32 x, Int32 y);
Une variable de type BinaryIntOp peut avoir une méthode ou un labmda qui lui est assigné, tant que la signature est la même: 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 tous les 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). Donc, si vous utilisez une grande variété de types de délégués, mais aucun 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 délégués (pas les méthodes elles-mêmes) avec la même signature mais des noms différents ne seront pas transtypés implicitement les uns vers les autres. Cela inclut les délégués Func et Action. Cependant, si la signature est identique, vous pouvez effectuer un cast explicitement 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 une astuce de compilateur. Si vous démarrez une instruction 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 qui peuvent être appelées sur 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 également la réponse de Karg illustrant la différence entre les délégués anonymes et les méthodes et 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 framework et de langage.
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 dans le 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 quelque part 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és anonymes, d'expressions lambda ou de fermetures - tout cela est défini par les spécifications du langage. Pensez à 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 maintenir l'état de fermeture; à .NET, il n'y a rien d'anonyme sur le délégué; c'est juste anonyme pour le programmeur C # qui l'écrit. C'est également vrai pour une expression lambda affectée à un type de délégué.
Qu'est - ce que .NET DOES comprendre est l'idée d'un délégué - un type qui décrit une signature de méthode, les instances qui correspondent soit à des appels liés à des méthodes de spécifiques sur des objets spécifiques, ou des appels non liés à une méthode particulière sur un type particulier qui peut être invoqué à l' encontre tout objet de ce type, où ladite méthode adhère à 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 des expressions de code - et qui peut donc également représenter des appels liés ou non à des méthodes sur des types ou des objets particuliers. Les instances 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 un pointeur de délégué vers elle est renvoyé).
En C #, vous pouvez produire des instances de types System.Expressions.Expression en affectant une expression lambda à une variable dudit type, qui produira le code approprié pour construire l'expression au moment de l'exécution.
Bien sûr, si vous étiez demandez quelle est la différence entre les expressions lambda et les méthodes anonymes en C #, après tout, alors tout cela est à peu près irelevant, et dans ce cas , la principale différence est la brièveté, qui se penche vers les délégués anonymes lorsque vous n » t se soucient des paramètres et ne prévoyez pas de retourner une valeur, et vers les lambdas lorsque vous voulez des paramètres inférenciés de type et des types de retour.
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 tandis qu'un lambda doit correspondre à la signature exacte. 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 qui n'a pas de paramètres ou une méthode qui n'a pas de paramètres. 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 / rappels (faites votre choix), et les lambdas sont des fonctions anonymes à peu près simplifiées. Du moins c'est ce que je dis aux gens.
Un délégué est toujours essentiellement un pointeur de fonction. Un lambda peut se transformer en délégué, mais il peut également se transformer en une arborescence 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 une arborescence 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";};
Delegate
from 'delegate', qui est un mot-clé.
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 Delegate
type est mal nommé, cependant; la création d'un objet de type Delegate
crée en fait une variable qui peut 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.
Un délégué est une file d'attente de pointeurs de fonction, 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 vers l'expression lambda en tant que méthode en le convertissant 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, le compilateur le castera pour vous. En l'utilisant à l'intérieur d'une instruction LINQ, le lambda sera traduit par le compilateur dans une arborescence d'expression au lieu d'un simple délégué.
La différence est vraiment qu'un lambda est un moyen laconique de définir une méthode à l'intérieur d'une autre expression, tandis qu'un délégué est un type d'objet réel.
Il est assez clair que la question était censée être "quelle est la différence entre les lambdas et les délégués anonymes ?" Sur toutes les réponses ici, une seule personne a bien compris - la principale différence est que les lambdas peuvent être utilisées 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 un typage nominal et l'implémentation d'une classe anonyme qui implémente une interface ou une classe abstraite, mais cela finit par être beaucoup de code lorsqu'une seule fonction est nécessaire.
Lambda vient de l'idée du calcul lambda de l'église d'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 du sucre syntaxique pour les délégués, je dirais que les délégués sont un pont pour faciliter les gens dans les lambdas en c #.
Quelques basiques 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 la méthode anonyme n'a pas de nom, nous avons besoin d'un délégué dans lequel nous pouvons attribuer ces deux méthodes ou expressions. Pour Ex.
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);
Les lambdas sont des versions simplifiées des délégués. Ils ont certaines des propriétés d'une fermeture comme les délégués anonymes, mais vous permettent également d'utiliser le typage implicite. Un lambda comme celui-ci:
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 un certain temps sur mon blog boiteux. Supposons que vous vouliez mettre à jour une étiquette à partir d'un thread de travail. J'ai 4 exemples de mise à jour de cette étiquette de 1 à 50 en utilisant des délégués, des délégués anon et 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, à cause de l'ambiguïté de votre question, car .NET n'obtient pas seul - c'est-à-dire sans c # - la compréhension des délégués et des expressions lambda.
Un délégué ( normal , par opposition aux délégués dits génériques , cf plus tard) doit être vu comme une sorte de c ++ typedef
d'un type pointeur de fonction, par exemple en c ++:
R (*thefunctionpointer) ( T ) ;
typedef est le type thefunctionpointer
qui est le type de pointeurs vers 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
où thefunction
serait une fonction prenant un T
et renvoyant un 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
où thefunction
serait une fonction prenant un T
et renvoyant un R
. Ceci est pour les délégués, dits délégués normaux.
Maintenant, vous avez aussi des délégués génériques en c #, qui sont des délégués qui sont génériques, c'est -à- dire qui sont pour ainsi dire "modelés", 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);
où thefunction2
est une fonction prenant comme argument et retournant un double
.
Imaginez maintenant qu'au lieu de cela, thefunction2
je voudrais utiliser une "fonction" qui n'est nulle part définie pour l'instant, par une instruction, et que je n'utiliserai jamais plus tard. Alors c # nous permet d'utiliser l' expression de cette fonction. Par l' expression je veux dire la « mathématique » (ou fonctionnelle, de coller aux programmes) l' expression de celui - ci, par exemple: à un j'associer le . En maths, vous écrivez ceci en utilisant le symbole latex "\ mapsto" . En c # la notation fonctionnelle a été emprunté: . Par exemple :double x
double
x*x
=>
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, contrairement aux délégués (génériques ou non).
Moralité ? À la fin, qu'est-ce qu'un délégué (resp. Délégué générique), sinon un type pointeur de fonction (resp. Type pointeur de fonction enveloppé + intelligent + générique), hein? Autre chose ! Voyez ceci et cela .
Eh bien, la version vraiment simplifiée est qu'un lambda n'est qu'un raccourci pour une fonction anonyme. Un délégué peut faire beaucoup plus que de simples fonctions anonymes: des choses comme des événements, des appels asynchrones et plusieurs chaînes de méthodes.