2017-07-09 22 views
0

说我有像这样调用一个MethodCallExpression与命名参数常数

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'

回答

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..." 
    } 
} 
+0

谢谢,但是选择双元表达权值调用拉姆达时没有考虑到的名称和参数位置。如果字典按错误的顺序读取,那么参数将被不同地解析。 – ron975

+0

@ ron975好吧,我更新了我的答案。 – Nikita

+0

谢谢,这很好! – ron975