c# - expresiones - ¿Cuál es la diferencia entre lambdas y delegados en.NET Framework?
if lambda c# (17)
Me hicieron mucho esta pregunta y pensé en solicitar algunos comentarios sobre cómo describir mejor la diferencia.
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.
delegar void PrintTestString (string testString); // declaro un delegado
PrintTestString print = (string testString) => {Console.WriteLine (testString); }; impresión();
Lo mismo con la expresión lambda. Usualmente necesitamos delegar para usarlos
s => s.Age> someValue && s.Age <algúnValor // devolverá verdadero / falso
Podemos usar un delegado de func para usar esta expresión.
Func <Estudiante, bool> checkStudentAge = s => s.Age> someValue && s.Age <someValue;
bool result = checkStudentAge (Student Object);
Bueno, la versión realmente simplificada es que una lambda es solo una abreviatura para una función anónima. Un delegado puede hacer mucho más que funciones anónimas: cosas como eventos, llamadas asincrónicas y cadenas de métodos múltiples.
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.
Los lambdas son muy similares a otros métodos, excepto por algunas diferencias sutiles.
- Un método normal se define en una "statement" y se vincula con un nombre permanente, mientras que una lambda se define "sobre la marcha" en una "expression" y no tiene un nombre permanente.
- Algunos 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 un método o una labmda asignados, 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 de Func y Action a menudo se consideran "tipos lambda", son como cualquier otro delegado. Lo bueno de ellos es que básicamente definen un nombre para cualquier tipo de delegado que pueda necesitar (hasta 4 parámetros, aunque ciertamente puede agregar más el suyo). Por lo tanto, si usa una gran variedad de tipos de delegados, pero ninguno más de una vez, puede evitar saturar su código con declaraciones de delegados 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 es saber que los tipos de delegados (no los métodos mismos) con la misma firma pero diferentes nombres no se intercambiará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 de delegado para crear esencialmente un objeto que represente esa función. Pero es realmente un truco de compilación. Si comienza un enunciado escribiendo el nombre de la función seguido de un punto (es decir, intente acceder a los miembros en la función), encontrará que no hay miembros allí para hacer referencia. Ni siquiera los de Objeto. 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 puedes hacer es extender la clase Delegate en sí misma, lo que seguramente también es útil, pero no tanto.
Actualización: vea también la respuesta de Karg que ilustra la diferencia entre los delegados anónimos y los métodos y lambdas.
Actualización 2: James Hart hace una nota importante, aunque muy técnica, de que lambdas y delegados no son entidades .NET (es decir, el CLR no tiene un concepto de delegado o lambda), sino que son construcciones de lenguaje y marco.
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 lo hizo bien: la principal diferencia 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
Heres un ejemplo que puse un rato en mi blog cojo. Digamos que quería actualizar una etiqueta de un hilo de trabajo. Tengo 4 ejemplos de cómo actualizar esa etiqueta del 1 al 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);
}
}
La pregunta es un poco ambigua, lo que explica la gran disparidad en las respuestas que recibe.
En realidad, preguntó cuál es la diferencia entre lambdas y delegados en el marco .NET; esa podría ser una de una serie de cosas. Estás preguntando:
¿Cuál es la diferencia entre las expresiones lambda y los delegados anónimos en el lenguaje C # (o VB.NET)?
¿Cuál es la diferencia entre los objetos System.Linq.Expressions.LambdaExpression y los objetos 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 C # expresiones Lambda y .NET System.Delegate?'', Que no tiene mucho sentido.
El .NET Framework no comprende por sí mismo los conceptos de delegados anónimos, expresiones lambda o cierres; todos estos elementos están definidos 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; a .NET, no hay nada anónimo sobre el delegado; es solo 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 ENTIENDE es la idea de un delegado, un tipo que describe una firma de método, cuyas instancias representan llamadas enlazadas a métodos específicos en objetos específicos, o llamadas no enlazadas a un método particular en un tipo particular que puede invocarse 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 introduce el espacio de nombres System.Linq.Expressions, que contiene clases para describir expresiones de código y que, por lo tanto, también pueden representar llamadas enlazadas o no ligadas a métodos en tipos u objetos particulares. Las instancias de LambdaExpression se pueden compilar en delegados reales (mediante lo cual un método dinámico basado en la estructura de la expresión se codifica y se devuelve un puntero de delegado).
En C # puede generar instancias de tipos System.Expressions.Expression asignando una expresión lambda a una variable de dicho tipo, que generará el código apropiado para construir la expresión en tiempo de ejecución.
Por supuesto, si preguntas cuál es la diferencia entre las expresiones lambda y los métodos anónimos en C #, después de todo, todo esto es bastante importante, y en ese caso la diferencia principal es la brevedad, que se inclina hacia los delegados anónimos cuando no lo haces. le importan los parámetros y no planea devolver un valor, y hacia lambdas cuando quiere tipos de parámetros de referencia y tipos de devolución.
Y las expresiones lambda admiten la generación de expresiones.
Lambdas son versiones simplificadas de delegados. Tienen algunas de las propiedades de un closure como los delegados anónimos, pero también le permiten usar la tipificación 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)
}
Las lambdas son simplemente azúcar sintáctico 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";};
Los delegados son equivalentes a punteros de función / punteros de método / devoluciones de llamada (haga su elección), y las lambdas son funciones anónimas simplificadas. Al menos eso es lo que le digo a la gente.
Los delegados son simplemente mecanografía estructural para funciones. Podría hacer lo mismo con el tipado nominal y la implementación de una clase anónima que implemente una interfaz o clase abstracta, pero eso termina siendo una gran cantidad de 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
Así que, aunque algunos podrían decir que lambda es azúcar sintáctica para los delegados, yo diría que los delegados son un puente para facilitar el ingreso de la gente a lambdas en c #.
No tengo mucha experiencia con esto, pero la forma en que lo describiría es que un delegado es un envoltorio alrededor de cualquier función, mientras que una expresión lambda es en sí misma una función anónima.
Un delegado es una cola de indicadores de función, la invocación de un delegado puede invocar múltiples métodos. Una lambda es esencialmente una declaración de método anónimo que puede ser interpretada por el compilador de forma diferente, dependiendo del contexto en que se usa.
Puede obtener un delegado que señale la expresión lambda como un método al convertirlo 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 lanzará por usted. Utilizándolo dentro de una declaración LINQ, el compilador traducirá la lambda en un árbol de expresiones en lugar de simplemente un delegado.
La diferencia 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.
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 a 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
está mal nombrado; crear un objeto de tipo Delegate
realmente crea una variable que puede contener funciones, ya sean lambdas, métodos estáticos o métodos de clase.
Un delegado es una referencia a un método con una lista de parámetros en particular y un tipo de retorno. Puede o no incluir un objeto.
Una expresión lambda es una forma de función anónima.
Un delegado siempre es básicamente un puntero a la función. Una lambda puede convertirse en un delegado, pero también puede convertirse en un árbol de expresiones 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.
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 maneras (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;
}
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 luego) delegado debe ser visto como una especie de tipo de typedef
c ++ de un tipo de puntero a 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 la función sería una función tomando una T
y devolviendo una 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 la función sería una función tomando una T
y devolviendo una 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 "modelados" por así decirlo, utilizando así una expresión de c ++. Ellos 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 imagina que en lugar de thefunction2
me gustaría utilizar una "función" que no está definida en ningún momento 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, atenerse a los programas) de la misma, por ejemplo: a un double x
voy a asociar el double
x*x
. En matemáticas, escribe esto usando el símbolo de látex "/ mapsto" . En c # la notación funcional ha sido prestada: =>
. Por ejemplo :
Func<double, double> thefunctor = ( (double x) => x * x ); // outer brackets are not
// mandatory
(double x) => x * x
es una expression . No es un tipo, mientras que los delegados (genéricos o no) sí lo son.
¿Moralidad? Al final, ¿qué es un delegado (o un delegado genérico), sino un tipo de puntero a la función (por ejemplo, envuelto + inteligente + tipo de puntero de función genérico), ¿eh? Algo más ! Ver this y that .