从MethodInfo创建的表达式树中参数的显式转换

Ian*_*cer 4 c# expression-trees

我有下面的方法将一个(非静态的)MethodInfo转换为一个编译后的表达式(Func),然后可以调用它。

这很好用:我可以使用同时包含引用对象和值类型的方法来调用它。

但是与原始方法不同,在原始方法中,我可以调用一个参数,该参数期望a double并将其传递给该方法,而int此编译表达式不支持该方法,并抛出InvalidCastException

如何修改它以支持正常方法调用期间发生的相同类型的隐式转换?

额外的问题:instanceExp是否应使用DeclaringType或中ReflectedTypeMethodInfo

public Func<object, object[], object> Create(MethodInfo methodInfo)
{
    var methodParams = methodInfo.GetParameters();
    var arrayParameter = Expression.Parameter(typeof(object[]), "array");

    var arguments =
        methodParams.Select((p, i) => Expression.Convert(
            Expression.ArrayAccess(arrayParameter, Expression.Constant(i)), p.ParameterType))
            .Cast<Expression>()
            .ToList();

    var instanceParameter = Expression.Parameter(typeof(object), "controller");

    var instanceExp = Expression.Convert(instanceParameter, methodInfo.DeclaringType);
    var callExpression = Expression.Call(instanceExp, methodInfo, arguments);

    var bodyExpression = Expression.Convert(callExpression, typeof(object));

    return Expression.Lambda<Func<object, object[], object>>(
        bodyExpression, instanceParameter, arrayParameter)
        .Compile();
}
Run Code Online (Sandbox Code Playgroud)

-编辑

可行的解决方案是:

var changeTypeMethod = typeof(Convert).GetMethod("ChangeType", new Type[] { typeof(object), typeof(TypeCode) });
var arguments =
     methodParams.Select((p, i) =>
         !typeof(IConvertible).IsAssignableFrom(p.ParameterType)
             // If NOT IConvertible, don't try to convert it
             ? (Expression)Expression.Convert(
                 Expression.ArrayAccess(arrayParameter, Expression.Constant(i)), p.ParameterType)
            :
             // Otherwise add an explicit conversion to the correct type to handle int <--> double etc.
            (Expression)Expression.Convert(
                Expression.Call(changeTypeMethod,
                    Expression.ArrayAccess(arrayParameter, Expression.Constant(i)),
                    Expression.Constant(Type.GetTypeCode(p.ParameterType))),
                p.ParameterType)
        )
        .ToList();
Run Code Online (Sandbox Code Playgroud)

das*_*ght 5

问题与这段C#代码相同:

object a = 123;
double b = (double)a; // InvalidCastException
Run Code Online (Sandbox Code Playgroud)

其原因是,aobject,所以为了使它成为一个double演员必须解开它,然后变换一个intdouble。该语言仅允许演员执行一件事-它不会展开或转换,但不能同时执行。你需要告诉编译器如何告诉它要做到这一点铸明确,有一个int内部的包裹object

double b = (double)((int)a); // Works
Run Code Online (Sandbox Code Playgroud)

如果您可以在LINQ表达式中执行相同的操作,则编译后的表达式也将起作用。但是,在生成表达式时,您可能不知道参数的实际类型,因此您可能需要采用其他策略-插入对Convert.ChangeType方法的调用,该方法可以同时展开和转换。