我目前正在尝试编写一些代码,将C#表达式转换为文本。
要做到这一点,我不仅需要遍历表达式树,还需要评估它的一小部分 - 以获取局部变量的当前值。
我发现很难说出来,所以这里是伪代码。缺少的部分是第一种方法:
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; }
}
我已经开始使用以下代码进行调查:
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级别,那么我对如何解析它并获得实际实例感到有些迷茫。
关于如何做到这一点的任何指示/建议都非常赞赏。
答案 0 :(得分:4)
解析表达式树是......复杂且耗时的。这是一个非常不完整的版本,只是处理您的示例。特别要注意我们需要:
输出:
一个新的输出集T2到:调用ExternalCalc从capture-context获取myEvaluator,输入=从@input获取T1
代码:
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());
}
}
答案 1 :(得分:2)
如果我理解正确,你会想知道如何从对象实例中获取属性,调用示例中的方法。正如Marc在他的回答中提到的那样,表达树的使用非常复杂且耗时,这特别针对你的例子(可能没有别的)。
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;
}
}