我需要使用仅在运行时可用的OrderBy<T, TKey>(Func<T, TKey>)
值调用TKey
方法。在阅读了关于如何将变量用作通用参数的答案之后,我正在尝试以下方法:
string key = "MyProperty";
Type keyType = typeof(T).GetProperty(key).PropertyType;
MethodInfo methodInfo = typeof(MyClass)
.GetMethod(
"MyGenericStaticMethod"),
BindingFlags.NonPublic | BindingFlags.Static);
// T is known at compile time.
MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(new[] { typeof(T), keyType});
var expression = genericMethodInfo.Invoke(null, new object[] { params });
myQueryable.OrderBy(expression);
问题是,genericMethodInfo.Invoke()
返回object
,因此不能与期望参数类型为OrderBy()
的{{1}}一起使用。但是,Func<T, TKey>
可以是不同的值类型,例如TKey
,string
,这些值类型仅在运行时才知道。甚至可以做到吗?如果可以,怎么办?
答案 0 :(得分:1)
方法MethodInfo.Invoke()
用于通过提供的参数执行方法调用,不能用于生成表达式。要生成可用作Expression
方法参数的lambda .OrderBy()
,请改用此参数:
string key = "MyProperty";
Type keyType = typeof(T).GetProperty(key).PropertyType;
MethodInfo methodInfo = typeof(MyClass)
.GetMethod(
"MyGenericStaticMethod",
BindingFlags.NonPublic | BindingFlags.Static);
MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(new[] { typeof(T), keyType });
//this represents parameter of keySelector expression used in OrderBy method
var parameterExpression = Expression.Parameter(typeof(T));
// Expression representing call to MyGenericStaticMethod
var expression = Expression.Call(genericMethodInfo, parameterExpression);
// To use it as an argument of OrderBy method, we must convert expression to lambda
var lambda = Expression.Lambda(expression, parameterExpression);
您可能会遇到另一个问题:您不能简单地调用myQueryable.OrderBy(lambda)
,因为这不允许编译器推断其通用参数,并且您不能提供这些通用参数,因为TKey
在编译时间。因此,您需要进行另一次反思,才能实际调用.OrderBy()
方法:
// OrderBy method has generic parameters and several overloads. It is thus
// difficult to get it's MethodInfo just by typeof(Queryable).GetMethod().
// Although it may seem weird, but it is easier to get it's MethodInfo from
// some arbitrary expression. Generic arguments "<object, object>" does not
// matter for now, we will replace them later
Expression<Func<IQueryable<object>, IQueryable<object>>> orderByExpression =
x => x.OrderBy<object, object>((o) => null);
// Replace generic parameters of OrderBy method with actual generic arguments
var orderByMethodInfo = (orderByExpression.Body as MethodCallExpression)
.Method
.GetGenericMethodDefinition()
.MakeGenericMethod(new[] { typeof(T), keyType });
// Now we are finally ready to call OrderBy method
var orderedResultQuery = orderByMethodInfo.Invoke(
null,
new Object[] { myQueryable, lambda })
as IQueryable<T>;
// Just for testing purpose, let's materialize result to list
var orderedResult = orderedResultQuery.ToList();