it-swarm-es.tech

¿Cuál es la diferencia entre lambdas y delegados en .NET Framework?

Me hicieron mucho esta pregunta y pensé en solicitar alguna información sobre cómo describir mejor la diferencia.

71
ScottKoon

En realidad son dos cosas muy diferentes. "Delegar" es en realidad el nombre de una variable que contiene una referencia a un método o una lambda, y una lambda es un método sin un nombre permanente.

Las lambdas son muy parecidas a otros métodos, excepto por un par de diferencias sutiles.

  1. Un método normal se define en un "instrucción" y se vincula a un nombre permanente, mientras que un lambda se define "sobre la marcha" en un "expresión" y no tiene permanente nombre.
  2. Algunas lambdas se pueden usar con árboles de expresión .NET, mientras que los métodos no.

Un delegado se define así:

delegate Int32 BinaryIntOp(Int32 x, Int32 y);

Una variable de tipo BinaryIntOp puede tener asignado un método o un labmda, siempre que la firma sea la misma: dos argumentos Int32 y un retorno Int32.

Una lambda podría definirse así:

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

Otra cosa a tener en cuenta es que aunque los tipos genéricos Func y Action a menudo se consideran "tipos lambda", son como cualquier otro delegado. Lo bueno de ellos es que esencialmente definen un nombre para cualquier tipo de delegado que pueda necesitar (hasta 4 parámetros, aunque ciertamente puede agregar más). Entonces, si está utilizando una amplia variedad de tipos de delegados, pero ninguno más de una vez, puede evitar abarrotar su código con declaraciones de delegado utilizando Func y Action.

Aquí hay una ilustración de cómo Func y Action son "no solo para lambdas":

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

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

Otra cosa útil que debe saber es que los tipos de delegado (no los métodos en sí mismos) con la misma firma pero con diferentes nombres no se convertirán implícitamente entre sí. Esto incluye a los delegados Func y Action. Sin embargo, si la firma es idéntica, puede emitir explícitamente entre ellos.

Haciendo un esfuerzo adicional ... En C # las funciones son flexibles, con el uso de lambdas y delegados. Pero C # no tiene "funciones de primera clase". Puede usar el nombre de una función asignada a una variable delegada para crear esencialmente un objeto que represente esa función. Pero es realmente un truco de compilación. Si comienza una declaración escribiendo el nombre de la función seguido de un punto (es decir, intente acceder a los miembros en la función en sí), encontrará que no hay miembros a los que hacer referencia. Ni siquiera los de Object. Esto evita que el programador haga cosas útiles (y potencialmente peligrosas, por supuesto), como agregar métodos de extensión que se pueden invocar en cualquier función. Lo mejor que puede hacer es extender la clase Delegate, que seguramente también es útil, pero no tanto.

Actualización: Consulte también Respuesta de Karg que ilustra la diferencia entre delegados anónimos frente a métodos y lambdas.

Actualización 2: James Hart hace una nota importante, aunque muy técnica, que las lambdas y los delegados no son entidades .NET (es decir, el CLR no tiene un concepto de delegado o lambda), sino que son marco y construcciones del lenguaje.

85
Chris Ammerman

La pregunta es un poco ambigua, lo que explica la gran disparidad en las respuestas que está obteniendo.

En realidad, usted preguntó cuál es la diferencia entre lambdas y delegados en el marco .NET; esa podría ser una de varias cosas. Estás preguntando:

  • ¿Cuál es la diferencia entre expresiones lambda y delegados anónimos en el lenguaje C # (o VB.NET)?

  • ¿Cuál es la diferencia entre los objetos System.Linq.Expressions.LambdaExpression y System.Delegate en .NET 3.5?

  • ¿O algo en algún lugar entre o alrededor de esos extremos?

Algunas personas parecen estar tratando de darle la respuesta a la pregunta '¿cuál es la diferencia entre las expresiones C # Lambda y .NET System.Delegate?', Lo cual no tiene mucho sentido.

El marco .NET en sí mismo no comprende los conceptos de delegados anónimos, expresiones lambda o cierres; esas son todas las cosas definidas por las especificaciones del lenguaje. Piense en cómo el compilador de C # traduce la definición de un método anónimo en un método en una clase generada con variables miembro para mantener el estado de cierre; para .NET, no hay nada anónimo sobre el delegado; es anónimo para el programador de C # que lo escribe. Eso es igualmente cierto para una expresión lambda asignada a un tipo de delegado.

Lo que .NET DOES entiende es la idea de un delegado, un tipo que describe la firma de un método, cuyas instancias representan llamadas vinculadas a métodos específicos en objetos específicos o llamadas no vinculadas a un método particular en un tipo particular que se puede invocar contra cualquier objeto de ese tipo, donde dicho método se adhiere a dicha firma. Todos estos tipos heredan de System.Delegate.

.NET 3.5 también presenta el espacio de nombres System.Linq.Expressions, que contiene clases para describir expresiones de código, y que, por lo tanto, también puede representar llamadas vinculadas o no vinculadas a métodos en tipos u objetos particulares. Las instancias de LambdaExpression se pueden compilar en delegados reales (mediante el cual se codifica un método dinámico basado en la estructura de la expresión y se devuelve un puntero de delegado).

En C # puede producir instancias de tipos System.Expressions.Expression asignando una expresión lambda a una variable de dicho tipo, que producirá el código apropiado para construir la expresión en tiempo de ejecución.

Por supuesto, si estabas preguntando cuál es la diferencia entre las expresiones lambda y los métodos anónimos en C #, después de todo, entonces todo esto es bastante irrelevante, y en ese caso la diferencia principal es la brevedad, que se inclina hacia delegados anónimos cuando no le interesan los parámetros y no planea devolver un valor, y hacia lambdas cuando desea parámetros de tipo inferenciado y tipos de retorno.

Y las expresiones lambda admiten la generación de expresiones.

28
James Hart

Una diferencia es que un delegado anónimo puede omitir parámetros, mientras que un lambda debe coincidir con la firma exacta. Dado:

public delegate string TestDelegate(int i);

public void Test(TestDelegate d)
{}

puede llamarlo de las siguientes cuatro formas (tenga en cuenta que la segunda línea tiene un delegado anónimo que no tiene ningún parámetro):

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

No puede pasar una expresión lambda que no tenga parámetros o un método que no tenga parámetros. Estos no están permitidos:

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

Los delegados son equivalentes a punteros de función/punteros de método/devoluciones de llamada (elija), y las lambdas son funciones anónimas bastante simplificadas. Al menos eso es lo que le digo a la gente.

13
Dan Shield

No tengo mucha experiencia con esto, pero la forma en que lo describiría es que un delegado es un contenedor alrededor de cualquier función, mientras que una expresión lambda es en sí misma una función anónima.

3
chessguy

Un delegado siempre es básicamente un puntero de función. Una lambda puede convertirse en un delegado, pero también puede convertirse en un árbol de expresión LINQ. Por ejemplo,

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

La primera línea produce un delegado, mientras que la segunda produce un árbol de expresión.

3
Curt Hagenlocher

Un delegado es una referencia a un método con una lista de parámetros particular y un tipo de retorno. Puede o no incluir un objeto.

Una expresión lambda es una forma de función anónima.

2
Peter Ritchie

las lambdas son simplemente azúcar sintáctica en un delegado. El compilador termina convirtiendo lambdas en delegados.

Estos son los mismos, creo:

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

Un delegado es una firma de función; algo como

delegate string MyDelegate(int param1);

El delegado no implementa un cuerpo.

La lambda es una llamada de función que coincide con la firma del delegado. Para el delegado anterior, puede usar cualquiera de;

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

Sin embargo, el tipo Delegate tiene un nombre incorrecto; crear un objeto de tipo Delegate en realidad crea una variable que puede contener funciones, ya sean lambdas, métodos estáticos o métodos de clase.

2
Steve Cooper

Está bastante claro que la pregunta era "¿cuál es la diferencia entre lambdas y delegados anónimos ?" De todas las respuestas aquí, solo una persona acertó: la diferencia principal es que las lambdas se pueden usar para crear árboles de expresión y delegados.

Puede leer más en MSDN: http://msdn.Microsoft.com/en-us/library/bb397687.aspx

2
Philip Beber

Un delegado es una cola de punteros de función, invocar a un delegado puede invocar múltiples métodos. Una lambda es esencialmente una declaración de método anónimo que el compilador puede interpretar de manera diferente, según el contexto en el que se utiliza.

Puede obtener un delegado que apunte a la expresión lambda como método convirtiéndolo en un delegado, o si lo pasa como un parámetro a un método que espera un tipo de delegado específico, el compilador lo convertirá por usted. Al usarlo dentro de una instrucción LINQ, el compilador traducirá la lambda a un árbol de expresión en lugar de simplemente a un delegado.

La diferencia realmente es que una lambda es una forma concisa de definir un método dentro de otra expresión, mientras que un delegado es un tipo de objeto real.

1
justin.m.chase

Los delegados son realmente solo tipos estructurales para funciones. Podría hacer lo mismo con la tipificación nominal y la implementación de una clase anónima que implemente una interfaz o una clase abstracta, pero que termine siendo mucho código cuando solo se necesita una función.

Lambda proviene de la idea del cálculo lambda de la Iglesia Alonzo en la década de 1930. Es una forma anónima de crear funciones. Se vuelven especialmente útiles para componer funciones

Entonces, aunque algunos podrían decir que la lambda es azúcar sintáctica para los delegados, yo diría que los delegados son un puente para facilitar a las personas a las lambdas en C #.

1
Steve g

Supongo que su pregunta se refiere a c # y no a .NET, debido a la ambigüedad de su pregunta, ya que .NET no se queda solo, es decir, sin c #, la comprensión de delegados y expresiones lambda.

A ( normal , en oposición a los llamados delegados genéricos , cf más adelante) delegado debe verse como una especie de c ++ typedef de un tipo de puntero de función, por ejemplo en c ++:

R (*thefunctionpointer) ( T ) ;

typedef es el tipo thefunctionpointer que es el tipo de punteros a una función que toma un objeto de tipo T y devuelve un objeto de tipo R. Lo usarías así:

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

donde thefunction sería una función que toma un T y devuelve un R.

En C # irías por

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

y lo usarías así:

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

donde thefunction sería una función que toma un T y devuelve un R. Esto es para delegados, llamados delegados normales.

Ahora, también tiene delegados genéricos en c #, que son delegados que son genéricos, es decir que están "con plantilla", por así decirlo, usando así una expresión de c ++ . Se definen así:

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

Y puedes usarlos así:

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

donde thefunction2 es una función que toma como argumento y devuelve un double.

Ahora imagine que en lugar de thefunction2 me gustaría usar una "función" que no está definida en ninguna parte por ahora, por una declaración, y que nunca usaré más adelante. Entonces c # nos permite usar la expresión de esta función. Por expresión me refiero a la expresión "matemática" (o funcional, para apegarse a los programas), por ejemplo: a un double x Asociaré el doublex*x. En matemáticas, escribe esto usando el símbolo de látex "\ mapsto" . En c # se ha tomado prestada la notación funcional: =>. Por ejemplo :

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

(double x) => x * x es una expresión . No es un tipo, mientras que los delegados (genéricos o no) lo son.

Moralidad? Al final, ¿qué es un delegado (resp. Delegado genérico), si no un tipo de puntero de función (resp. Envuelto + inteligente + tipo de puntero de función genérico), ¿eh? Algo más ! Ver esto y eso .

Aquí hay un ejemplo que puse en mi blog aburrido. Supongamos que desea actualizar una etiqueta de un subproceso de trabajo. Tengo 4 ejemplos de cómo actualizar esa etiqueta de 1 a 50 usando delegados, anon delegados y 2 tipos 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); 
         } 
     }
0
Echostorm

Algunos básicos aquí. "Delegar" es en realidad el nombre de una variable que contiene una referencia a un método o una lambda

Este es un método anónimo:

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

Como el método anónimo no tiene ningún nombre, necesitamos un delegado en el que podamos asignar ambos métodos o expresiones. Por ej.

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

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

Lo mismo con la expresión lambda. Por lo general, necesitamos delegados para usarlos

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

Podemos usar un delegado func para usar esta expresión.

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

bool result = checkStudentAge ( Student Object);
0
Yogesh Prajapati

Las lambdas son versiones simplificadas de delegados. Tienen algunas de las propiedades de un cierre como delegados anónimos, pero también le permiten utilizar la escritura implícita. Una lambda como esta:

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

es mucho más conciso que lo que puedes hacer con un delegado:

something.Sort(sortMethod);
...

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