【问题标题】:Invoking a MethodCallExpression with named parameter constants使用命名参数常量调用 MethodCallExpression
【发布时间】:2017-07-09 20:52:50
【问题描述】:

假设我有这样的功能

public string TestValue(string hello, Guid world)
{
    return hello + world;
}

假设objectParams 是对象Dictionary<string, object> 的字典,将参数名称映射到值,我目前正在将参数值与方法名称匹配,如下所示:

var method = this.GetType().GetMethod("TestValue");
var methodParameters = method.GetParameters();
var paramMatcher = (from paramValue in objectParams
                    from methodParam in methodParameters
                    where param.Key == clrParam.Name
                    select (name: clrParam.Name,
                            type: clrParam.ParameterType,
                            value: paramValue.Value));

然后我构建表达式来调用方法TestValue,就像这样,但这是我遇到问题的部分。

var paramExpress = (from param in paramMatcher
                    select Expression.Assign(Expression.Parameter(param.type, param.name), Expression.Constant(param.value)));

Func<object> result = Expression.Lambda<Func<object>(Expression.Call(Expression.Constant(this),
    method, paramExpress)).Compile();
var res = result.Invoke(); //should return 'somestringxxxxxxx-xxxx...etc'

问题是我不能保证参数值是按调用顺序的,所以我想依靠参数的名称来调用。我不确定如何正确地将常量值分配给它们的参数表达式。运行此代码会导致编译 lambda 时出现异常 System.InvalidOperationException: 'variable 'hello' of type 'System.String' referenced from scope '', but it is not defined'

【问题讨论】:

    标签: c# dynamic reflection lambda expression


    【解决方案1】:

    Expression.Assign是一个二元运算,所以左边的变量取了一个表达式右边计算的新值。

    在这些方面:

    var paramExpress = (from param in paramMatcher
         select Expression.Assign(Expression.Parameter(param.type, param.name),
                    Expression.Constant(param.value, param.type)));
    
    Func<object> result = Expression.Lambda<Func<object>(Expression.Call(Expression.Constant(this),
        method, paramExpress)).Compile();
    

    您已收到但未使用实际参数值,显示在二进制表达式的右侧部分中。

    解决方案:

    public class C
    {
        public string TestValue(string hello, Guid world)
        {
            return hello + world;
        }
    
        public string Execute()
        {
            var objectParams = new Dictionary<string, object>()
            {
                {"hello", "somestring"},
                {"world", Guid.NewGuid()}
            };
            var method = this.GetType().GetMethod("TestValue");
            var methodParameters = method.GetParameters();
            var paramMatcher = (from paramValue in objectParams
                from methodParam in methodParameters
                where paramValue.Key == methodParam.Name
                orderby methodParam.Position  // <-- preserves original order
                select (name: methodParam.Name,
                type: methodParam.ParameterType,
                value: paramValue.Value));
    
            var paramExpress = (from param in paramMatcher
                 select Expression.Assign(Expression.Parameter(param.type, param.name),
                        Expression.Constant(param.value, param.type)));
    
            var values = paramExpress.Select(v => v.Right); // !!!
    
            Func<string> result = Expression.Lambda<Func<string>>(Expression.Call(Expression.Constant(this),
                method, values)).Compile();
            return result.Invoke(); // returns "somestringxxxxxxx-xxxx..."
        }
    }
    

    【讨论】:

    • 谢谢,但是在调用lambda时选择二进制表达式的Right值并没有考虑到参数的名称和位置。如果字典以错误的顺序读取,则参数的解析方式会有所不同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-29
    • 1970-01-01
    • 2012-10-15
    • 2018-06-03
    • 2012-04-19
    • 1970-01-01
    相关资源
    最近更新 更多