delegar palabra clave vs notación lambda

Respuestas:

140

Respuesta corta: no.

Respuesta más larga que puede no ser relevante:

  • Si asigna la lambda a un tipo de delegado (como FuncoAction ), obtendrá un delegado anónimo.
  • Si asigna la lambda a un tipo de expresión, obtendrá un árbol de expresión en lugar de un delegado anónimo. El árbol de expresión se puede compilar a un delegado anónimo.

Editar: Aquí hay algunos enlaces para Expresiones.

  • System.Linq.Expression.Expression (TDelegate) (comience aquí).
  • Linq en memoria con delegados (como System.Func) usa System.Linq.Enumerable . Linq to SQL (y cualquier otra cosa) con expresiones usa System.Linq.Queryable . Echa un vistazo a los parámetros de esos métodos.
  • Una explicación de ScottGu . En pocas palabras, Linq en memoria producirá algunos métodos anónimos para resolver su consulta. Linq to SQL producirá un árbol de expresión que representa la consulta y luego traducirá ese árbol a T-SQL. Linq to Entities producirá un árbol de expresión que representa la consulta y luego traducirá ese árbol al SQL apropiado de la plataforma.
Amy B
fuente
3
¿Un tipo de expresión? Esto me suena a territorio nuevo. ¿Dónde puedo encontrar más información sobre los tipos de expresión y el uso de árboles de expresión en C #?
MojoFilter
2
Respuesta aún más larga: hay razones difíciles por las que también son convertibles a diferentes tipos de delegados :)
Jon Skeet
Tenga en cuenta que la lambda solo se puede asignar a un tipo de expresión, si es una expresión lambda.
Micha Wiedenmann
125

Me gusta la respuesta de Amy, pero pensé que sería pedante. La pregunta dice: "Una vez que se compila", lo que sugiere que ambas expresiones se han compilado. ¿Cómo podrían ambos compilar, pero con uno convertido en un delegado y otro en un árbol de expresión? Es complicado: debe usar otra característica de los métodos anónimos; el único que no es compartido por las expresiones lambda. Si especifica un método anónimo sin especificar una lista de parámetros en absoluto de que es compatible con cualquier tipo de delegado de regresar nula y sin ningúnout parámetros. Armados con este conocimiento, deberíamos poder construir dos sobrecargas para que las expresiones sean completamente inequívocas pero muy diferentes.

¡Pero ocurre un desastre! Al menos con C # 3.0, no puede convertir una expresión lambda con un cuerpo de bloque en una expresión, ni puede convertir una expresión lambda con una asignación en el cuerpo (incluso si se usa como valor de retorno). Esto puede cambiar con C # 4.0 y .NET 4.0, lo que permite que se exprese más en un árbol de expresión. En otras palabras, con los ejemplos que MojoFilter dio, los dos casi siempre se convertirán en lo mismo. (Más detalles en un minuto).

Sin embargo, podemos usar el truco de los parámetros de delegado si cambiamos un poco los cuerpos:

using System;
using System.Linq.Expressions;

public class Test
{
    static void Main()
    {
        int x = 0;
        Foo( () => x );
        Foo( delegate { return x; } );
    }

    static void Foo(Func<int, int> action)
    {
        Console.WriteLine("I suspect the anonymous method...");
    }

    static void Foo(Expression<Func<int>> func)
    {
        Console.WriteLine("I suspect the lambda expression...");
    }
}

¡Pero espera! Podemos diferenciar entre los dos incluso sin usar árboles de expresión, si somos lo suficientemente astutos. El siguiente ejemplo utiliza las reglas de resolución de sobrecarga (y el truco de coincidencia de delegado anónimo) ...

using System;
using System.Linq.Expressions;

public class Base
{
    public void Foo(Action action)
    {
        Console.WriteLine("I suspect the lambda expression...");
    }
}

public class Derived : Base
{
    public void Foo(Action<int> action)
    {
        Console.WriteLine("I suspect the anonymous method...");
    }
}

class Test
{
    static void Main()
    {
        Derived d = new Derived();
        int x = 0;
        d.Foo( () => { x = 0; } );
        d.Foo( delegate { x = 0; } );
    }
}

Ay. Recuerde niños, cada vez que sobrecarga un método heredado de una clase base, un gatito comienza a llorar.

Jon Skeet
fuente
9
Saqué mis palomitas y leí todo. Esa es una distinción en la que probablemente nunca pensaría, incluso si la estuviera mirando directamente a la cara.
MojoFilter
27
Sabía algo de esto, pero debo felicitarte por tu habilidad para comunicarlo a los humanos.
Amy B
1
Para cualquier persona interesada en los cambios en .NET 4.0 (basado en el CTP), consulte marcgravell.blogspot.com/2008/11/future-expressions.html . Tenga en cuenta que C # 4.0 todavía no hace nada nuevo por lo que puedo decir.
Marc Gravell
44
Jon, eres genial. Erik, para ser un verdadero fanático de Skeet, deberías estar suscrito a su stack overflow rss como yo. Simplemente inserte stackoverflow.com/users/22656 en su lector de feeds.
Paul Batum el
3
@RoyiNamir: Si utiliza un método anónimo sin una lista de parámetros, eso es compatible con cualquier tipo de delegado con parámetros que no sean de ref / out, siempre que el tipo de retorno sea compatible. Básicamente estás diciendo "No me importan los parámetros". Tenga en cuenta que nodelegate { ... } es lo mismo que delegate() { ... }: este último solo es compatible con un tipo de delegado sin parámetros.
Jon Skeet
2

En los dos ejemplos anteriores no hay diferencia, cero.

La expresion:

() => { x = 0 }

es una expresión Lambda con cuerpo de declaración, por lo que no se puede compilar como un árbol de expresión De hecho, ni siquiera se compila porque necesita un punto y coma después de 0:

() => { x = 0; } // Lambda statement body
() => x = 0      // Lambda expression body, could be an expression tree. 
Olmo
fuente
66
Seguramente eso significa que hay una diferencia de "uno compilará, el otro no";)
Jon Skeet
2

Amy B tiene razón. Tenga en cuenta que puede haber ventajas al usar árboles de expresión. LINQ to SQL examinará el árbol de expresión y lo convertirá a SQL.

También puede jugar trucos con lamdas y árboles de expresión para pasar efectivamente los nombres de los miembros de la clase a un marco de una manera segura de refactorización. Moq es un ejemplo de esto.

Daniel Plaisted
fuente
-1

Hay una diferencia

Ejemplo:

var mytask = Task.Factory.StartNew(() =>
{
    Thread.Sleep(5000);
    return 2712;
});
mytask.ContinueWith(delegate
{
    _backgroundTask.ContinueTask(() =>lblPercent.Content = mytask.Result.ToString(CultureInfo.InvariantCulture));
});   

Y lo reemplazo con lambda: (error)

var mytask = Task.Factory.StartNew(() =>
{
    Thread.Sleep(5000);
    return 2712;
});
mytask.ContinueWith(()=>
{
    _backgroundTask.ContinueTask(() =>lblPercent.Content = mytask.Result.ToString(CultureInfo.InvariantCulture));
});
Án Bình Trọng
fuente
Incorrecto ~ lambda falló solo porque la firma del parámetro del método no coincide.
Jack Wang
-1

Algunos conceptos básicos aquí.

Este es un método anónimo.

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

Como los métodos anónimos no tienen nombres, necesitamos un delegado en el que podamos asignar ambos métodos o expresiones. p.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 un delegado 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);
Yogesh Prajapati
fuente