I am looking to create a cachable lambda expression just from a methodinfo object retrieved via Type.GetMethod() without coding a typed cast of the function.
I have gotten everthing to work except for the cast from an compiled expression to an typed invokable function.
var parameters = Array.ConvertAll(method.GetParameters(), input => Expression.Parameter(input.ParameterType));
var instanceExp = Expression.Constant(_implementation);
var call = Expression.Call(instanceExp, method, parameters);
var exp = Expression.Lambda(call, parameters).Compile();
What is missing is:
Func<T1,T2,T3> castExp = (Func<T1,T2,T3>)exp;
What I would like to do is cast to a function with a specific number of parameters without specifying the specic type:
Func<object,object,object> castExp = (Func<object,object,object>)exp;
This way I could call exp(o1, o2, o3) without ever coding the types of o1 etc. But there is a runtime error casting a function of type Func to Func.
How can I cast the function to some form of func<,,> that allows for passing parameters of unspecified type?
(Btw.: It is not an option to change the signature of the methods which are to be called.)
-1I have partially solved the issue I had.
What I was able to do is create expressions for methods of unknown signature if the method does not use in/out/ref parameters. In that case I had to fall back to the MethodInfo.Invoke call.
private object CallMethod(MethodInfo method, object obj, object[] parameters) {
// Methods with void as return must be cast to Action instead of Function
var voidMethod = voidMethod = method.ReturnType == typeof(void);
// Methods with ref parameters can be called but the parameters won't work.
var refMethod = Array.FindAll(method.GetParameters(), info => info.ParameterType.IsByRef;
var paramExprs = getParamExpr(method);
var paramTypes = getParamTypes(method, paramExprs);
var instanceExp = Expression.Convert(paramExprs[0], method.DeclaringType);
Expression call = null;
if (voidMethod) {
call = Expression.Call(instanceExp, method, paramTypes);
} else {
call = Expression.Convert(Expression.Call(instanceExp, method, paramTypes), typeof(object));
}
exp = Expression.Lambda(call, paramExprs).Compile();
if (voidMethod) {
switch (method.GetParameters().Length) {
case 0:
((Action<object>)exp)(_obj);
break;
case 1:
((Action<object, object>)exp)(_obj, parameters[0]);
break;
// Continue here with more case statements.
}
} else {
switch (method.GetParameters().Length) {
case 0:
result = ((Func<object, object>)exp)(_obj);
break;
case 1:
result = ((Func<object, object, object>)exp)(_obj, parameters[0]);
break;
// Continue here with more case statements
}
}
// Error handling omited
return result;
}
private List<ParameterExpression> getParamExpr(MethodInfo method) {
var list = new List<ParameterExpression>();
list.Add(Expression.Parameter(typeof(object), "obj"));
list.AddRange(Array.ConvertAll(method.GetParameters(), input => Expression.Parameter(typeof(object))));
return list;
}
private List<Expression> getParamTypes(MethodInfo method, List<ParameterExpression> inList) {
var list = new List<Expression>();
var methParams = method.GetParameters();
list.AddRange(
// Skip the first item as this is the object on which the method is called.
inList.Skip(1).Select(
input => Expression.Convert(
input,
Type.GetType(
methParams[inList.IndexOf(input)-1].ParameterType.FullName.Replace("&", string.Empty)))));
return list;
}
I hope it is complete as I have left out a lot of boilerplate for error handling etc.
The expression object can be cached but have to go through the casting everytime you want to call them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With