Obtenir la valeur d'exécution d'une ParameterExpression dans une arborescence d'expression

c# c#-4.0 delegates expression-trees lambda

Question

Il me manque l'évidence: comment accéder à la valeur d'un paramètre dans un arbre d'expression d'expression lambda?

Scénario: pour un délégué x, je crée dynamiquement une expression lambda avec un corps d'arborescence d'expression qui a la même signature que le délégué x. À l'intérieur du corps de lamdba, je fais quelques validations, vérifications, journalisations (il ne s'agit que de tester le code, pas la production), puis j'appelle le délégué d'origine x avec les paramètres d'origine. Si le délégué a une valeur de retour, celle-ci est également renvoyée.

Cela fonctionne assez bien (y compris la transmission des paramètres au délégué d'origine).

Mais je frappe un mur de briques si je veux accéder aux valeurs de paramètre d'origine transmises au délégué / lambda.

pseudo code:

var del = new Func<string, int>(_=> {return 42;});
var paramDefs = Array.ConvertAll<ParameterInfo, ParameterExpression>(del.Method.GetParameters(), _ => { return Expression.Parameter(_.ParameterType, _.Name); });
var variableTest = Expression.Variable(typeof(string), "str");

var expression = Expression.Block(
  new [] { variableTest },
  // this line assigns the actual run time value (which is what I need) of the parameter to the variable - but I cannot hardcode the index.
  //Expression.Assign(variableTest, paramDefs[0]) 
  // this line would assigns the ParameterExpression object (causing a run-time exception since the type of the variable is string) ... I need the _value_ of the first (or nth) parameter.
  Expression.Assign(variableTest, Expression.ArrayIndex(Expression.Constant(paramDefs), Expression.Constant(0)))
);
var lamdba = Expression.Lambda(del.GetType(), expression, "foo", paramDefs);
var del2 = lamdba.Compile() as Func<string, int>;
del2("this is a test");

Réponse acceptée

On dirait que vous avez trop confondu le compilateur d’arbres d’expression (eh bien, ce code m’a aussi dérouté) Je peux voir ce que vous avez essayé de faire: vous avez obtenu un élément d'un tableau, puis vous avez décidé de le parcourir en boucle. Mais vous ne pouviez pas faire de tableau [ParameterExpression], vous avez donc utilisé ArrayIndex. Mais...

Mais ArrayIndex, en fait, ne renvoie pas "chaîne". Il retourne MethodCallExpression. Ainsi, dans cette expression "Assign", vous avez en réalité ParameterExpression et MethodCallExpression. Le compilateur ET est suffisamment intelligent pour compiler ces expressions et essayer d’affecter des résultats. Mais le résultat de votre MethodCallExpression est ParameterExpression. Quand vous aviez paramDefs [0], vous aviez immédiatement ParameterExpression et le compilateur pouvait le gérer. Mais la compilation d'expressions imbriquées est plus difficile et il est totalement difficile de savoir si vous voulez vraiment compiler cette expression imbriquée ou non.

Ce que vous pouvez faire est de compiler et d'appeler vous-même la MethodCallExpression. Ainsi, vous aurez ParameterExpression dans l'expression Assign (comme auparavant). Cela peut ressembler à ceci:

// Replace Assign in your Block expression.
Expression.Assign(variableTest, Expression.Lambda<Func<ParameterExpression>>(Expression.ArrayIndex(Expression.Constant(paramDefs), Expression.Constant(0))).Compile()()),

Mais cela pourrait être très lourd en termes de performances (plus le code est moche). Donc, je m'en tiens à votre idée de tirer la boucle de l'arbre d'expression.




Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi