Reputation: 2636
Say I have some function like so
public string TestValue(string hello, Guid world)
{
return hello + world;
}
Assuming that objectParams
is a dictionary of object Dictionary<string, object>
that map the parameter name to the value, I'm currently matching the parameter values to the method name like so:
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));
I then build the expression to call the method TestValue
like so, but this is the part I'm having trouble with.
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'
The issue is that I can't guarantee that the parameter values are in call order so I want to rely on the name of the parameters to call. I'm not sure on how to assign the constant values to their parameter expressions properly. Running this code results in the exception System.InvalidOperationException: 'variable 'hello' of type 'System.String' referenced from scope '', but it is not defined'
when compiling the lambda.
Upvotes: 0
Views: 674
Reputation: 900
Expression.Assign is a binary operation, so the variable in the left part takes a new value calculated in the right part of expression.
On these lines:
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();
you've received and just didn't used actual parameters values, presented in the right parts of binary expressions.
Solution:
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..."
}
}
Upvotes: 1