web-dev-qa-db-fra.com

mot-clé délégué contre notation lambda

Une fois compilé, y a-t-il une différence entre:

delegate { x = 0; }

et

() => { x = 0 }

?

175
MojoFilter

Réponse courte: non.

Réponse plus longue qui peut ne pas être pertinente:

  • Si vous assignez le lambda à un type de délégué (tel que Func ou Action), vous obtiendrez un délégué anonyme.
  • Si vous affectez le lambda à un type d'expression, vous obtiendrez une arborescence d'expression au lieu d'un délégué anonyme. L'arbre d'expression peut ensuite être compilé en un délégué anonyme.

Edit: Voici quelques liens pour les expressions.

  • System.Linq.Expression.Expression (TDelegate) (commencez ici).
  • Linq en mémoire avec des délégués (tels que System.Func) utilise System.Linq.Enumerable . Linq to SQL (et toute autre chose) avec des expressions utilise System.Linq.Queryable . Vérifiez les paramètres sur ces méthodes.
  • Un Explication de ScottG . En un mot, Linq in-memory produira des méthodes anonymes pour résoudre votre requête. Linq to SQL produira un arbre d'expression qui représente la requête, puis le traduira en T-SQL. Linq to Entities produira un arbre d'expression qui représente la requête, puis traduira cet arbre en SQL approprié pour la plate-forme.
137
Amy B

J'aime la réponse de David, mais je pensais que je serais pédant. La question dit "Une fois compilé" - ce qui suggère que les deux expressions ont ont été compilées. Comment pourraient-ils tous les deux compiler, mais avec l'un converti en délégué et l'autre en arbre d'expression? C'est une question délicate: vous devez utiliser une autre fonctionnalité des méthodes anonymes; le seul qui ne soit pas partagé par les expressions lambda. Si vous spécifiez une méthode anonyme sans spécifier de liste de paramètres, du tout elle est compatible avec tout type de délégué renvoyant void et sans aucun paramètre out. Forts de cette connaissance, nous devrions pouvoir construire deux surcharges pour rendre les expressions complètement non ambiguë mais très différentes.

Mais la catastrophe frappe! Au moins avec C # 3.0, vous ne pouvez pas convertir une expression lambda avec un corps de bloc en une expression. Vous ne pouvez pas non plus convertir une expression lambda avec une affectation dans le corps (même si elle est utilisée comme valeur de retour). Cela peut changer avec C # 4.0 et .NET 4.0, ce qui permet d’en exprimer davantage dans un arbre d’expression. Donc, en d'autres termes, avec les exemples donnés par MojoFilter, les deux seront presque toujours convertis en la même chose. (Plus de détails dans une minute.)

Nous pouvons utiliser l'astuce des paramètres de délégué si nous modifions un peu les corps:

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

Mais attendez! Nous pouvons différencier les deux sans même utiliser d’arbres d’expression, si nous sommes assez rusés. L'exemple ci-dessous utilise les règles de résolution de surcharge (et l'astuce de correspondance de délégué anonyme) ...

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

Aie. N'oubliez pas les enfants, chaque fois que vous surchargez une méthode héritée d'une classe de base, un petit chaton se met à pleurer.

122
Jon Skeet

Dans les deux exemples ci-dessus, il n'y a pas de différence, zéro.

L'expression:

() => { x = 0 }

est une expression Lambda avec un corps d'instruction, elle ne peut donc pas être compilée sous forme d'arborescence d'expression. En fait, il ne compile même pas car il faut un point-virgule après 0:

() => { x = 0; } // Lambda statement body
() => x = 0      // Lambda expression body, could be an expression tree. 
2
Olmo

David B a raison. Notez qu'il peut être avantageux d'utiliser des arbres d'expression. LINQ to SQL examinera l’arbre d’expression et le convertira en SQL.

Vous pouvez également jouer à des astuces avec des lamdas et des arbres d'expression pour transmettre efficacement les noms des membres de la classe à une structure de manière sécurisée pour le refactoring. Moq en est un exemple.

2
Daniel Plaisted