Extraction de la valeur actuelle d’une variable d’instance en parcourant une expression

J’essaie actuellement d’écrire du code qui transforme les expressions C # en texte.

Pour ce faire, je dois non seulement parcourir l’arbre d’expression, mais aussi en évaluer une petite partie – afin d’obtenir la valeur actuelle d’une variable locale.

Je trouve très difficile de mettre des mots, voici donc le pseudo-code. La partie manquante est dans la première méthode:

public class Program { private static void DumpExpression(Expression expression) { // how do I dump out here some text like: // set T2 = Perform "ExternalCalc" on input.T1 // I can easily get to: // set T2 = Perform "Invoke" on input.T1 // but how can I substitute Invoke with the runtime value "ExternalCalc"? } static void Main(ssortingng[] args) { var myEvaluator = new Evaluator() {Name = "ExternalCalc"}; Expression<Func> myExpression = (input) => new Output() {T2 = myEvaluator.Invoke(input.T1)}; DumpExpression(myExpression); } } class Evaluator { public ssortingng Name { get; set; } public ssortingng Invoke(ssortingng input) { throw new NotImplementedException("Never intended to be implemented"); } } class Input { public ssortingng T1 { get; set; } } class Output { public ssortingng T2 { get; set; } } 

J’ai commencé à étudier cela en utilisant un code comme:

  foreach (MemberAssignment memberAssignment in body.Bindings) { Console.WriteLine("assign to {0}", memberAssignment.Member); Console.WriteLine("assign to {0}", memberAssignment.BindingType); Console.WriteLine("assign to {0}", memberAssignment.Expression); var expression = memberAssignment.Expression; if (expression is MethodCallExpression) { var methodCall = expression as MethodCallExpression; Console.WriteLine("METHOD CALL: " + methodCall.Method.Name); Console.WriteLine("METHOD CALL: " + expression.Type.Name); var target = methodCall.Object; // ? } } 

mais une fois que j’arrive à ce niveau MethodCallExpression, je me sens un peu perdu quant à la façon de l’parsingr et d’obtenir ensuite l’instance réelle.

Tous les indicateurs / suggestions sur la façon de procéder sont très appréciés.

L’parsing des arbres d’expression est … complexe et prend du temps. Voici une version très incomplète qui traite votre exemple à peu près. Notons en particulier que nous devons:

  • code dur à Evaluator, car “ExternalCalc” ne fait pas partie de l’expression
  • évaluer manuellement une partie de l’arbre

Sortie:

une nouvelle sortie définie sur T2 pour: appeler ExternalCalc sur get myEvaluator à partir de capture-context avec input = get T1 à partir de @input

Code:

 private static void DumpExpression(Expression expression) { var sb = new SsortingngBuilder(); Walk(expression, sb); ssortingng s = sb.ToSsortingng(); } static object Evaluate(Expression expr) { switch (expr.NodeType) { case ExpressionType.Constant: return ((ConstantExpression)expr).Value; case ExpressionType.MemberAccess: var me = (MemberExpression)expr; object target = Evaluate(me.Expression); switch (me.Member.MemberType) { case System.Reflection.MemberTypes.Field: return ((FieldInfo)me.Member).GetValue(target); case System.Reflection.MemberTypes.Property: return ((PropertyInfo)me.Member).GetValue(target, null); default: throw new NotSupportedException(me.Member.MemberType.ToSsortingng()); } default: throw new NotSupportedException(expr.NodeType.ToSsortingng()); } } static void Walk(Expression expr, SsortingngBuilder output) { switch (expr.NodeType) { case ExpressionType.New: var ne = (NewExpression)expr; var ctor = ne.Constructor; output.Append(" a new ").Append(ctor.DeclaringType.Name); if(ne.Arguments != null && ne.Arguments.Count != 0) { var parameters = ctor.GetParameters(); for(int i = 0 ;i < ne.Arguments.Count ; i++) { output.Append(i == 0 ? " with " : ", ") .Append(parameters[i].Name).Append(" ="); Walk(ne.Arguments[i], output); } } break; case ExpressionType.Lambda: Walk(((LambdaExpression)expr).Body, output); break; case ExpressionType.Call: var mce = (MethodCallExpression)expr; if (mce.Method.DeclaringType == typeof(Evaluator)) { object target = Evaluate(mce.Object); output.Append(" call ").Append(((Evaluator)target).Name); } else { output.Append(" call ").Append(mce.Method.Name); } if (mce.Object != null) { output.Append(" on"); Walk(mce.Object, output); } if (mce.Arguments != null && mce.Arguments.Count != 0) { var parameters = mce.Method.GetParameters(); for (int i = 0; i < mce.Arguments.Count; i++) { output.Append(i == 0 ? " with " : ", ") .Append(parameters[i].Name).Append(" ="); Walk(mce.Arguments[i], output); } } break; case ExpressionType.MemberInit: var mei = (MemberInitExpression)expr; Walk(mei.NewExpression, output); foreach (var member in mei.Bindings) { switch(member.BindingType) { case MemberBindingType.Assignment: output.Append(" set ").Append(member.Member.Name) .Append(" to:"); Walk(((MemberAssignment)member).Expression, output); break; default: throw new NotSupportedException(member.BindingType.ToString()); } } break; case ExpressionType.Constant: var ce = (ConstantExpression)expr; if (Attribute.IsDefined(ce.Type, typeof(CompilerGeneratedAttribute))) { output.Append(" capture-context"); } else { output.Append(" ").Append(((ConstantExpression)expr).Value); } break; case ExpressionType.MemberAccess: var me = (MemberExpression)expr; output.Append(" get ").Append(me.Member.Name).Append(" from"); if (me.Expression == null) { // static output.Append(me.Member.DeclaringType.Name); } else { Walk(me.Expression, output); } break; case ExpressionType.Parameter: var pe = (ParameterExpression)expr; output.Append(" @").Append(pe.Name); break; default: throw new NotSupportedException(expr.NodeType.ToString()); } } 

Si je comprends bien, vous vous demandez comment obtenir des propriétés à partir de l’instance de l’object sur lequel la méthode de votre exemple était appelée. Comme Marc le mentionne dans sa réponse, les arbres d’expression sont complexes et prennent du temps, cela concerne spécifiquement votre exemple (et probablement rien d’autre).

  private static void DumpExpression(Expression expression) { var lambda = expression as LambdaExpression; if(lambda != null) { DumpExpression(lambda.Body); return; } var init = expression as MemberInitExpression; if(init != null) { foreach(var binding in init.Bindings) { var assignment = (MemberAssignment) binding; DumpExpression(assignment.Expression); return; } } var methodCallExpression = expression as MethodCallExpression; if(methodCallExpression != null) { //Create a func that resortingeves the real value of the object the method call // is being evaluated on and get the Name property from it var objectGetExpression = Expression.Lambda>(methodCallExpression.Object); var objectGetFunc = objectGetExpression.Comstack(); Console.WriteLine(objectGetFunc().Name); return; } }