C# 如何使用对参数的操作编译MethodCallExpression?

C# 如何使用对参数的操作编译MethodCallExpression?,c#,lambda,expression-trees,C#,Lambda,Expression Trees,我有一个方法 private static int Method(int n) { return n; } 我在ExpressionVisitor中的重写VisitMethodCall中获得MethodCallExpression。MethodCallExpression包含: n => Method(2 + n) 我想把它编译成Func,然后像这样调用: func(3) public class ParametersExtractorVisitor : Expression

我有一个方法

private static int Method(int n)
{
    return n;
}
我在ExpressionVisitor中的重写VisitMethodCall中获得MethodCallExpression。MethodCallExpression包含:

n => Method(2 + n)
我想把它编译成Func,然后像这样调用:

func(3)
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);
    }
}
它应该返回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));
IEnumerable parameters=expression.Arguments.Select(a=>expression.Parameter(a.Type,a.ToString());
MethodCallExpression调用=Expression.call(Expression.Method,参数);
表达式lambda=Expression.lambda(call,call.Arguments.OfType());
var func=lambda.Compile();
控制台写入线(func(3));
这是给我3分,不是5分


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

不确定为什么要这样做,但无论如何,要这样做,您需要提取
方法调用表达式使用的参数(不是参数)。为此,您可以这样滥用expression visitor:

func(3)
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);
    }
}
公共类参数sextractorvisitor:ExpressionVisitor{
public IList ExtractedParameters{get;}=new List();
受保护的重写表达式VisitParameter(ParameterExpression节点){
ExtractedParameters.Add(节点);
返回基本访问参数(节点);
}
}
然后在代码中这样使用它:

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));
var visitor=新参数sextractorvisitor();
参观(表达);
MethodCallExpression调用=Expression.call(Expression.Method,Expression.Arguments);
表达式lambda=Expression.lambda(调用,visitor.ExtractedParameters);
var func=lambda.Compile();
控制台写入线(func(3));

实现它不需要访问者。
基本上,您的
方法
函数应该提供传递给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;
    }
}
使用系统;
使用System.Linq.Expressions;
运用系统反思;
名称空间测试
{
班级计划
{
静态void Main(字符串[]参数){
var method=typeof(Program.GetMethod(“method”,BindingFlags.Static | BindingFlags.Public);
var参数=表达式参数(typeof(int),“n”);
var add=Expression.add(Expression.Constant(2,typeof(int)),参数);
var methodCallExpression=Expression.Call(null,method,add);
var lambda=Expression.lambda(methodCallExpression,参数);
var func=lambda.Compile();
控制台写入线(func(3));
}
公共静态int方法(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;
    }
}
使用系统;
使用System.Linq;
使用System.Linq.Expressions;
运用系统反思;
名称空间测试
{
类MethodCallVisitor:ExpressionVisitor
{
私有只读int-toAdd;
公共方法调用访问者(int-toAdd){
this.toAdd=toAdd;
}
受保护的重写表达式VisitMethodCall(MethodCallExpression节点){
var add=Expression.add(node.Arguments.First(),Expression.Constant(toAdd));
返回表达式.Call(node.Object、node.Method、add);
}
}
班级计划
{
静态void Main(字符串[]参数){
var methodCallVisitor=newmethodcallvisitor(2);
var method=typeof(Program.GetMethod(“method”,BindingFlags.Static | BindingFlags.Public);
var参数=表达式参数(typeof(int),“n”);
var methodCallExpression=Expression.Call(null、方法、参数);
var lambda=Expression.lambda(methodCallExpression,参数);
lambda=(表达式)methodCallVisitor.Visit(lambda);
var func=lambda.Compile();
控制台写入线(func(3));
}
公共静态int方法(int n)=>n;
}
}

为什么要在覆盖expression visitor时执行类似操作?这只是一个教育示例,我会将此函数发送到visitor外部。我认为您需要另一个expression visitor。您需要从MethodCallExpression提取\替换参数(而不是参数)。我对它进行了研究,但它不包含任何关于二进制操作的信息。对于二进制操作,您可以使用Expression.MakeBinary工厂,或者只使用特定的Expression.Add。请参阅下面我的答案,因为我的目标是将一个方法替换为另一个方法,以便在控制台上打印此方法的启动时间SOP只有MethodCallExpression,它是更大表达式树的一部分。据我所知,他只想用这个方法调用lambda。我明白了!我差一点,该死!非常感谢你!