我有下面的方法,將(非靜態) MethodInfo
轉換為編譯後的表達式( Func
)然後我可以調用它。
這很好用:我可以用一個期望引用對象和值類型的方法來調用它。
但是與原始方法不同,我可以調用一個方法,該方法有一個期望為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();
}
---編輯
工作解決方案是:
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();
問題與這段C#代碼中的問題相同:
object a = 123;
double b = (double)a; // InvalidCastException
原因是a
是一個object
,所以為了使它成為double
,強制轉換必須將其解包,然後將int
轉換為double
。該語言允許演員只做一件事 - 它將打開或轉換,但不是兩者。您需要通過告訴編譯器在object
內部包含一個int
來告訴編譯器如何顯式執行此操作:
double b = (double)((int)a); // Works
如果您可以在LINQ表達式中執行相同的操作,則編譯後的表達式也可以正常工作。但是,您可能不知道生成表達式時參數的實際類型,因此您可能希望採用不同的策略 - 在調用Convert.ChangeType
方法時連接,該方法可以同時解包和轉換。