Retrieving the current value of an instance variable while executing an expression

I'm currently trying to write code that turns C # expressions into text.

To do this, I need to not only go through the Expression tree, but also evaluate only a small part of it - to get the current value of the local variable.

It’s very difficult for me to put it in words, so instead there is pseudocode. The invalid part is in the first method:

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(string[] args) { var myEvaluator = new Evaluator() {Name = "ExternalCalc"}; Expression<Func<Input, Output>> myExpression = (input) => new Output() {T2 = myEvaluator.Invoke(input.T1)}; DumpExpression(myExpression); } } class Evaluator { public string Name { get; set; } public string Invoke(string input) { throw new NotImplementedException("Never intended to be implemented"); } } class Input { public string T1 { get; set; } } class Output { public string T2 { get; set; } } 

I started researching this using code:

  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; // ? } } 

but as soon as I get to this level of MethodCallExpression, I feel a little lost on how to parse it and then get the actual instance.

Any pointers / suggestions on how to do this are greatly appreciated.

+4
source share
2 answers

Parsing tree expressions ... is complex and time-consuming. Here's a very incomplete version that just handles your example. In particular, please note that we need:

  • hard-code for Evaluator because "ExternalCalc" is not part of the expression
  • manually evaluate part of a tree

Output:

new T2 output dataset: call ExternalCalc to get myEvaluator from capture-input context with input = get T1 from @input

code:

 private static void DumpExpression(Expression expression) { var sb = new StringBuilder(); Walk(expression, sb); string s = sb.ToString(); } 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.ToString()); } default: throw new NotSupportedException(expr.NodeType.ToString()); } } static void Walk(Expression expr, StringBuilder 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()); } } 
+3
source

If I understood correctly, you are wondering how to get the properties from the instance of the object on which the method was called in your example. As Mark points out in his answer, expression trees are complex and take a lot of time to work, this specifically indicates your example (and probably nothing more).

  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 retrieves the real value of the object the method call // is being evaluated on and get the Name property from it var objectGetExpression = Expression.Lambda<Func<Evaluator>>(methodCallExpression.Object); var objectGetFunc = objectGetExpression.Compile(); Console.WriteLine(objectGetFunc().Name); return; } } 
+2
source

Source: https://habr.com/ru/post/1442265/


All Articles