5

私は現在、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レベルに到達すると、それを解析して実際のインスタンスを取得する方法について少し迷っています。

これを行う方法についてのポインタ/提案は非常に高く評価されています。

4

2 に答える 2

4

式ツリーの解析は... 複雑で時間がかかります。これは、例をほぼ処理する非常に不完全なバージョンです。特に、次のことが必要であることに注意してください。

  • 「ExternalCalc」は式の一部ではないため、Evaluator にハードコードします。
  • ツリーの一部を手動で評価する

出力:

新しい出力セット T2 to: get myEvaluator from capture-context with input = get T1 from @input で ExternalCalc を呼び出す

コード:

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());
    }
}
于 2012-10-26T08:28:04.263 に答える
2

私が正しく理解していれば、あなたの例のメソッドが呼び出されていたオブジェクトのインスタンスからプロパティを取得する方法を疑問に思っているでしょう。マークが回答で述べているように、式ツリーは複雑で作業に時間がかかります。これは具体的にあなたの例に対応しています (おそらく他には何もありません)。

    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;
        }
    }
于 2012-10-26T08:33:21.257 に答える