Passer d'un lambda à une expression est facile en utilisant un appel de méthode ...
public void GimmeExpression(Expression<Func<T>> expression)
{
((MemberExpression)expression.Body).Member.Name; // "DoStuff"
}
public void SomewhereElse()
{
GimmeExpression(() => thing.DoStuff());
}
Mais je voudrais transformer le Func en une expression, seulement dans de rares cas ...
public void ContainTheDanger(Func<T> dangerousCall)
{
try
{
dangerousCall();
}
catch (Exception e)
{
// This next line does not work...
Expression<Func<T>> DangerousExpression = dangerousCall;
var nameOfDanger =
((MemberExpression)dangerousCall.Body).Member.Name;
throw new DangerContainer(
"Danger manifested while " + nameOfDanger, e);
}
}
public void SomewhereElse()
{
ContainTheDanger(() => thing.CrossTheStreams());
}
La ligne qui ne fonctionne pas me donne l'erreur de compilation Cannot implicitly convert type 'System.Func<T>' to 'System.Linq.Expressions.Expression<System.Func<T>>'
. Une distribution explicite ne résout pas la situation. Y a-t-il une installation pour ce faire que je néglige?
Ooh, ce n'est pas facile du tout. Func<T>
représente un delegate
générique et non une expression. S'il y a un moyen de le faire (en raison des optimisations et d'autres choses effectuées par le compilateur, certaines données peuvent être supprimées, il peut donc être impossible de récupérer l'expression d'origine), ce serait le démontage de l'IL à la volée et inférer l'expression (ce qui n'est pas du tout facile). Traitement des expressions lambda en tant que données (Expression<Func<T>>
) est une magie effectuée par le compilateur (fondamentalement, le compilateur construit un arbre d'expression en code au lieu de le compiler en IL).
C'est pourquoi les langages qui poussent les lambdas à l'extrême (comme LISP) sont souvent plus faciles à implémenter comme interprètes. Dans ces langues, le code et les données sont essentiellement la même chose (même à au moment de l'exécution ), mais notre puce ne peut pas comprendre cette forme de code, nous avons donc pour émuler une telle machine en construisant un interprète au-dessus d'elle qui la comprend (le choix fait par LISP comme les langages) ou en sacrifiant la puissance (le code ne sera plus exactement égal aux données) dans une certaine mesure (le choix fait par C #) . En C #, le compilateur donne l'illusion de traiter le code comme des données en permettant aux lambdas d'être interprétés comme code (Func<T>
) et données (Expression<Func<T>>
) à temps de compilation .
private static Expression<Func<T, bool>> FuncToExpression<T>(Func<T, bool> f)
{
return x => f(x);
}
Ce que vous devriez probablement faire, c'est inverser la méthode. Prenez une Expression>, puis compilez et exécutez. S'il échoue, vous avez déjà l'expression à examiner.
public void ContainTheDanger(Expression<Func<T>> dangerousCall)
{
try
{
dangerousCall().Compile().Invoke();;
}
catch (Exception e)
{
// This next line does not work...
var nameOfDanger =
((MemberExpression)dangerousCall.Body).Member.Name;
throw new DangerContainer(
"Danger manifested while " + nameOfDanger, e);
}
}
public void SomewhereElse()
{
ContainTheDanger(() => thing.CrossTheStreams());
}
De toute évidence, vous devez prendre en compte les implications en termes de performances et déterminer si c'est quelque chose que vous devez vraiment faire.
Cependant, vous pouvez aller dans l'autre sens via la méthode .Compile () - vous ne savez pas si cela vous est utile:
public void ContainTheDanger<T>(Expression<Func<T>> dangerousCall)
{
try
{
var expr = dangerousCall.Compile();
expr.Invoke();
}
catch (Exception e)
{
Expression<Func<T>> DangerousExpression = dangerousCall;
var nameOfDanger = ((MethodCallExpression)dangerousCall.Body).Method.Name;
throw new DangerContainer("Danger manifested while " + nameOfDanger, e);
}
}
public void SomewhereElse()
{
var thing = new Thing();
ContainTheDanger(() => thing.CrossTheStreams());
}
INJection.Lambda Converter est une bibliothèque qui convertit les délégués en expression
public class Program
{
private static void Main(string[] args) {
var lambda = Lambda.TransformMethodTo<Func<string, int>>()
.From(() => Parse)
.ToLambda();
}
public static int Parse(string value) {
return int.Parse(value)
}
}
Si vous avez parfois besoin d'une expression et parfois d'un délégué, vous avez 2 options:
Expression<...>
, et juste .Compile().Invoke(...)
si vous voulez un délégué. Évidemment, cela a coûté. Expression<Func<T>> ToExpression<T>(Func<T> call)
{
MethodCallExpression methodCall = call.Target == null
? Expression.Call(call.Method)
: Expression.Call(Expression.Constant(call.Target), call.Method);
return Expression.Lambda<Func<T>>(methodCall);
}
JB Evain de l'équipe Cecil Mono fait des progrès pour permettre cette
http://evain.net/blog/articles/2009/04/22/converting-delegates-to-expression-trees