如何使用参数操作编译MethodCallExpression?

时间:2017-12-15 11:47:24

标签: c# lambda expression-trees

我有方法方法

private static int Method(int n)
{
    return n;
}

我在ExpressionVisitor中覆盖了VisitMethodCall中的MethodCallExpression。 MethodCallExpression包含:

n => Method(2 + n)

我想把它编译成Func并像这样调用:

func(3)

它应该返回5.

我试过了:

    IEnumerable<ParameterExpression> parameters = expression.Arguments.Select(a => Expression.Parameter(a.Type, a.ToString()));
    MethodCallExpression call = Expression.Call(expression.Method, parameters);
    Expression<Func<Int32, Int32>> lambda = Expression.Lambda<Func<int, int>>(call, call.Arguments.OfType<ParameterExpression>());
    var func = lambda.Compile();
    Console.WriteLine(func(3));

它让我回归3而不是5

这是因为2 + x是param名称,我将其替换为3。

2 个答案:

答案 0 :(得分:1)

不确定为什么要这样做,但无论如何要做到这一点,你需要提取MethodCallExpression使用的参数(而不是参数)。为此你可以像这样滥用表达访问者:

public class ParametersExtractorVisitor : ExpressionVisitor {
    public IList<ParameterExpression> ExtractedParameters { get; } = new List<ParameterExpression>();
    protected override Expression VisitParameter(ParameterExpression node) {
        ExtractedParameters.Add(node);
        return base.VisitParameter(node);
    }
}

然后在你的代码中使用它:

var visitor = new ParametersExtractorVisitor();
visitor.Visit(expression);
MethodCallExpression call = Expression.Call(expression.Method, expression.Arguments);
Expression<Func<Int32, Int32>> lambda = Expression.Lambda<Func<int, int>>(call, visitor.ExtractedParameters);
var func = lambda.Compile();
Console.WriteLine(func(3));

答案 1 :(得分:1)

您不需要访问者来实施它 基本上,你的Method函数应该提供传递给lambda的值的加法运算结果,其常量值为2.

using System;
using System.Linq.Expressions;
using System.Reflection;

namespace Test
{
    class Program
    {
        static void Main(string[] args) {
            var method = typeof(Program).GetMethod("Method", BindingFlags.Static | BindingFlags.Public);
            var parameter = Expression.Parameter(typeof(int), "n");
            var add = Expression.Add(Expression.Constant(2, typeof(int)), parameter);
            var methodCallExpression = Expression.Call(null, method, add);
            var lambda = Expression.Lambda<Func<int, int>>(methodCallExpression, parameter);
            var func = lambda.Compile();

            Console.WriteLine(func(3));
        }

        public static int Method(int n) => n;
    }
}

我会通过改变MethodCallExpression

来使用访问者来实现它
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace Test
{

    class MethodCallVisitor : ExpressionVisitor
    {
        private readonly int toAdd;

        public MethodCallVisitor(int toAdd) {
            this.toAdd = toAdd;
        }

        protected override Expression VisitMethodCall(MethodCallExpression node) {
            var add = Expression.Add(node.Arguments.First(), Expression.Constant(toAdd));

            return Expression.Call(node.Object, node.Method, add);
        }
    }

    class Program
    {
        static void Main(string[] args) {
            var methodCallVisitor = new MethodCallVisitor(2);
            var method = typeof(Program).GetMethod("Method", BindingFlags.Static | BindingFlags.Public);
            var parameter = Expression.Parameter(typeof(int), "n");
            var methodCallExpression = Expression.Call(null, method, parameter);
            var lambda = Expression.Lambda<Func<int, int>>(methodCallExpression, parameter);

            lambda = (Expression<Func<int, int>>)methodCallVisitor.Visit(lambda);
            var func = lambda.Compile();

            Console.WriteLine(func(3));
        }

        public static int Method(int n) => n;
    }
}