Ian*_*cer 4 c# expression-trees
我有下面的方法将一个(非静态的)MethodInfo转换为一个编译后的表达式(Func),然后可以调用它。
这很好用:我可以使用同时包含引用对象和值类型的方法来调用它。
但是与原始方法不同,在原始方法中,我可以调用一个参数,该参数期望a double并将其传递给该方法,而int此编译表达式不支持该方法,并抛出InvalidCastException。
如何修改它以支持正常方法调用期间发生的相同类型的隐式转换?
额外的问题:instanceExp是否应使用DeclaringType或中ReflectedType的MethodInfo?
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)
问题与这段C#代码相同:
object a = 123;
double b = (double)a; // InvalidCastException
Run Code Online (Sandbox Code Playgroud)
其原因是,a是object,所以为了使它成为一个double演员必须解开它,然后变换一个int来double。该语言仅允许演员执行一件事-它不会展开或转换,但不能同时执行。你需要告诉编译器如何告诉它要做到这一点铸明确,有一个int内部的包裹object:
double b = (double)((int)a); // Works
Run Code Online (Sandbox Code Playgroud)
如果您可以在LINQ表达式中执行相同的操作,则编译后的表达式也将起作用。但是,在生成表达式时,您可能不知道参数的实际类型,因此您可能需要采用其他策略-插入对Convert.ChangeType方法的调用,该方法可以同时展开和转换。
| 归档时间: |
|
| 查看次数: |
689 次 |
| 最近记录: |