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

Réponses:

140

Réponse courte: non.

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

  • Si vous affectez le lambda à un type de délégué (tel que Funcou Action), vous obtiendrez un délégué anonyme.
  • Si vous affectez le lambda à un type d'expression, vous obtiendrez une arborescence d'expressions au lieu d'un délégué anonyme. L'arbre d'expression peut ensuite être compilé vers 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 de ces méthodes.
  • Une explication de ScottGu . En un mot, Linq en mémoire 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 traduira cet arbre en T-SQL. Linq to Entities produira un arbre d'expression qui représente la requête, puis traduira cet arbre en SQL approprié à la plate-forme.
Amy B
la source
3
Un type d'expression? Cela me semble être un nouveau territoire. Où puis-je en savoir plus sur les types d'expressions et l'utilisation des arbres d'expressions en C #?
MojoFilter
2
Réponse encore plus longue - il y a aussi des raisons délicates pour lesquelles ils sont convertibles en différents types de délégués :)
Jon Skeet
Notez que le lambda ne peut être affecté qu'à un type Expression, s'il s'agit d'une expression lambda.
Micha Wiedenmann
125

J'aime la réponse d'Amy, mais je pensais que je serais pédante. La question dit: "Une fois compilée" - ce qui suggère que les deux expressions ont été compilées. Comment pourraient-ils tous les deux compiler, mais l'un étant 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 n'est pas partagé par les expressions lambda. Si vous spécifiez une méthode anonyme sans spécifier une liste de paramètres du tout , il est compatible avec tout type de délégué retour non avenue et sans aucun outparamètre. Forts de ces connaissances, nous devrions être capables de construire deux surcharges pour rendre les expressions totalement sans ambiguïté 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 - ni 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, qui permettent d'exprimer davantage dans une arborescence d'expression. Donc, en d'autres termes, avec les exemples que MojoFilter a donné, 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 changeons 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 même sans 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. Rappelez-vous les enfants, chaque fois que vous surchargez une méthode héritée d'une classe de base, un petit chaton se met à pleurer.

Jon Skeet
la source
9
J'ai sorti mon pop-corn et j'ai tout lu. C'est une distinction à laquelle je ne penserais probablement jamais même si je la regardais droit en face.
MojoFilter
27
J'en savais une partie, mais je dois vous féliciter pour votre capacité à le communiquer aux humains.
Amy B
1
Pour toute personne intéressée par les changements dans .NET 4.0 (basé sur le CTP) - voir marcgravell.blogspot.com/2008/11/future-expressions.html . Notez que C # 4.0 ne fait encore rien de nouveau pour autant que je sache.
Marc Gravell
4
Jon, tu es rock. Erik, pour être un vrai fanboi de Skeet, vous devriez être abonné à son stack overflow rss comme je le suis. Collez simplement stackoverflow.com/users/22656 dans votre lecteur de flux.
Paul Batum
3
@RoyiNamir: Si vous utilisez une méthode anonyme sans liste de paramètres, elle est compatible avec tout type de délégué avec des paramètres non ref / out, tant que le type de retour est compatible. En gros, vous dites "Je me fiche des paramètres". Notez que ce delegate { ... }n'est pas la même chose que delegate() { ... }- ce dernier n'est compatible qu'avec un type de délégué sans paramètre.
Jon Skeet
2

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 en tant qu'arbre d'expression. En fait, il ne compile même pas car il a besoin d'un point-virgule après 0:

() => { x = 0; } // Lambda statement body
() => x = 0      // Lambda expression body, could be an expression tree. 
Olmo
la source
6
Cela signifie sûrement qu'il y a une différence entre "l'un compilera, l'autre non";)
Jon Skeet
2

Amy B a raison. Notez qu'il peut y avoir des avantages à utiliser des arbres d'expression. LINQ to SQL examinera l'arborescence des expressions et la convertira en SQL.

Vous pouvez également jouer des tours avec des lamdas et des arbres d'expressions pour transmettre efficacement les noms des membres de la classe à un framework d'une manière sûre pour le refactoring. Moq en est un exemple.

Daniel Plaisted
la source
-1

Il existe une différence

Exemple:

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

Et je remplace par lambda: (erreur)

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
la source
Wrong ~ lambda a échoué simplement parce que la signature du paramètre de méthode ne correspond pas.
Jack Wang
-1

Quelques notions de base ici.

Ceci est une méthode anonyme

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

Comme les méthodes anonymes n'ont pas de nom, nous avons besoin d'un délégué dans lequel nous pouvons attribuer ces deux méthodes ou expressions. par exemple

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);
Yogesh Prajapati
la source