使用具有未知签名的MethodInfo创建表达式函数

时间:2013-12-10 09:55:42

标签: c# generics casting lambda

我希望仅通过Type.GetMethod()检索的methodinfo对象创建一个可缓存的lambda表达式,而无需对该函数的类型转换进行编码。

除了从编译表达式转换为类型化的可调用函数之外,我已经开始工作了。

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();

缺少的是:

Func<T1,T2,T3> castExp =  (Func<T1,T2,T3>)exp;

我想要做的是转换为具有特定数量参数的函数,而不指定特定类型:

Func<object,object,object> castExp =  (Func<object,object,object>)exp;

这样我就可以调用exp(o1,o2,o3)而无需编写o1等类型。 但是存在运行时错误,将Func类型的函数强制转换为Func。

如何将函数转换为某种形式的函数&lt; ,,&gt;允许传递未指定类型的参数?

(顺便说一下:不能更改要调用的方法的签名。)

2 个答案:

答案 0 :(得分:1)

-1我已经部分解决了我的问题。

如果方法不使用in / out / ref参数,我能够为未知签名的方法创建表达式。在那种情况下,我不得不回到MethodInfo.Invoke调用。

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;
}

我希望它完整,因为我遗漏了大量的错误处理样板。

表达式对象可以被缓存,但每次要调用它们时都必须经过转换。

答案 1 :(得分:0)

我不是100%确定你想做什么,但你可以创建一个能够返回你改变的功能的函数:

Func<T1, T2, T3> GetAlteredFunction<T1, T2, T3>(Func<T1, T2, T3> func)
{
    //execute your logic and return the result
}

所以你可以打电话给

Func<object,object,object> castExp =  GetAlteredFunction(exp);