Je comprends les lambdas et les Func
y Action
délégués. Mais les expressions me laissent perplexe.
Dans quelles circonstances utiliseriez-vous un Expression<Func<T>>
plutôt qu'un simple Func<T>
?
Je comprends les lambdas et les Func
y Action
délégués. Mais les expressions me laissent perplexe.
Dans quelles circonstances utiliseriez-vous un Expression<Func<T>>
plutôt qu'un simple Func<T>
?
Lorsque vous souhaitez traiter les expressions lambda comme des arbres d'expression et regarder à l'intérieur de ceux-ci au lieu de les exécuter. Par exemple, LINQ to SQL récupère l'expression et la convertit en une instruction SQL équivalente qu'il soumet au serveur (au lieu d'exécuter l'expression lambda).
Sur le plan conceptuel, Expression<Func<T>>
es complètement différent de Func<T>
. Func<T>
désigne un delegate
qui est en fait un pointeur vers une méthode et Expression<Func<T>>
désigne un structure de données arborescente pour une expression lambda. Cette structure arborescente décrit ce que fait une expression lambda plutôt que de passer à l'acte. Il contient essentiellement des données sur la composition des expressions, des variables, des appels de méthode, ... (par exemple, il contient des informations telles que ce lambda est une certaine constante + un certain paramètre). Vous pouvez utiliser cette description pour la convertir en une méthode réelle (avec Expression.Compile
) ou de faire d'autres choses (comme l'exemple LINQ to SQL). Le fait de traiter les lambdas comme des méthodes anonymes et des arbres d'expression est purement lié à la compilation.
Func<int> myFunc = () => 10; // similar to: int myAnonMethod() { return 10; }
sera effectivement compilée en une méthode IL qui ne reçoit rien et renvoie 10.
Expression<Func<int>> myExpression = () => 10;
sera convertie en une structure de données décrivant une expression qui ne reçoit aucun paramètre et renvoie la valeur 10 :
Bien qu'ils aient tous deux la même apparence au moment de la compilation, ce que le compilateur génère est totalement différent .
En d'autres termes, un Expression
contient les méta-informations relatives à un délégué donné.
@bertl En fait, non. Le délégué n'est pas du tout impliqué. La raison pour laquelle il y a une association avec un délégué est que vous pouvez compiler l'expression à un délégué - ou plus précisément, le compiler en une méthode et obtenir le délégué de cette méthode comme valeur de retour. Mais l'arbre d'expression lui-même n'est qu'une donnée. Le délégué n'existe pas lorsque vous utilisez Expression<Func<...>>
au lieu de Func<...>
.
Ces métadonnées sont utiles pour convertir une requête C# en une requête dans n'importe quel autre langage. Par exemple, vous pouvez convertir une requête C# StartsWith()
en SQL LIKE '%etc'
. Voir codethinked.com/taking-the-magic-out-of-expression
Une considération extrêmement importante dans le choix entre Expression et Func est que les fournisseurs IQueryable comme LINQ to Entities peuvent 'digérer' ce que vous passez dans une Expression, mais ignoreront ce que vous passez dans un Func. J'ai publié deux articles de blog sur le sujet :
En savoir plus sur Expression vs Func avec Entity Framework y Tomber amoureux de LINQ - Partie 7 : Expressions et fonctions (la dernière section)
J'aimerais ajouter quelques notes sur les différences entre Func<T>
y Expression<Func<T>>
:
Func<T>
est juste un MulticastDelegate normal de la vieille école ;Expression<Func<T>>
est une représentation de l'expression lambda sous la forme d'un arbre d'expression ;Func<T>
;ExpressionVisitor
;Func<T>
;Expression<Func<T>>
.Il y a un article qui décrit les détails avec des exemples de code :
LINQ : Func<T> vs. Expression<Func<T>> .
J'espère que cela vous sera utile.
Jolie liste, mais vous mentionnez que la conversion inverse est possible, alors que l'inverse exact ne l'est pas. Certaines métadonnées sont perdues au cours du processus de conversion. Cependant, vous pouvez le décompiler en un arbre d'expression qui produit le même résultat lorsqu'il est compilé à nouveau.
LINQ en est l'exemple canonique (par exemple, pour communiquer avec une base de données), mais en réalité, chaque fois que vous vous souciez davantage de l'expression de l'information, vous pouvez utiliser la méthode ce que à faire, plutôt que de le faire réellement. Par exemple, j'utilise cette approche dans la pile RPC de protobuf-net (pour éviter la génération de code, etc.) - vous appelez donc une méthode avec :
string result = client.Invoke(svc => svc.SomeMethod(arg1, arg2, ...));
Cette opération déconstruit l'arbre d'expression pour résoudre les problèmes suivants SomeMethod
(et la valeur de chaque argument), effectue l'appel RPC, met à jour les éventuels ref
/ out
et renvoie le résultat de l'appel à distance. Cela n'est possible que via l'arbre d'expression. J'en parle plus en détail aquí .
Un autre exemple est celui où vous construisez manuellement les arbres d'expression dans le but de les compiler en une lambda, comme le fait l'option opérateurs génériques code.
Vous utiliserez une expression lorsque vous voudrez traiter votre fonction comme des données et non comme du code. Vous pouvez le faire si vous souhaitez manipuler le code (en tant que données). La plupart du temps, si vous ne voyez pas l'utilité des expressions, vous n'avez probablement pas besoin d'en utiliser.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
22 votes
Func<> sera converti en méthode au niveau du compilateur c# ,Expression<Func<>> sera exécuté au niveau MSIL après avoir compilé le code directement, c'est la raison pour laquelle il est plus rapide.
1 votes
En plus des réponses, la spécification du langage csharp "4.6 expression tree types" est une référence utile.
1 votes
Pour tous ceux qui souhaitent faire des références croisées avec la spécification du langage C# : Types d'arbres d'expression