提取一个实例变量的当前值边走边表达式 [英] Extracting the current value of an instance variable while walking an Expression
问题描述
我目前正在写一些代码,原来的C#表达成文字。
I am currently trying to write some code which turns C# Expressions into text.
要做到这一点,我不仅要穿行表达式树,也评估它只是一小部分 - 为了得到一个局部变量的当前值
To do this, I need to not only walk through the Expression tree, but also evaluate just a little part of it - in order to get the current value of a local variable.
我发现很难言喻,所以这里是伪-code来代替。缺少的部分是在第一种方法:
I am finding very hard to put into words, so here is the pseudo-code instead. The missing 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 have started investigating this using code like:
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;
// ?
}
}
但一旦我得到了MethodCallExpression的水平,那么我感觉有点失去了关于如何分析它,并随后得到实际的实例。
but once I get to that MethodCallExpression level then I am feeling a bit lost about how to parse it and to then get the actual instance.
任何指针/如何做到这一点非常赞赏。
Any pointers/suggestions on how to do this very much appreciated.
推荐答案
解析表达式树是...复杂和费时。下面是一个非常不完整版本刚刚有关处理您的例子。特别要注意的是,我们需要:
Parsing expression trees is... complex and time-consuming. Here's a very incomplete version that just-about handles your example. In particular, note that we need to:
- 硬编码到求值,因为ExternalCalc不是表达式的一部分
- 手动评估一些树
输出:
一个新的输出设置T2到:从输入捕捉上下文调用ExternalCalc上获取myEvaluator =从@input
a new Output set T2 to: call ExternalCalc on get myEvaluator from capture-context with input = get T1 from @input
代码:
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());
}
}
这篇关于提取一个实例变量的当前值边走边表达式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!