【问题标题】:How to call IQueryable.OrderBy() method with a runtime type for generic parameter?如何使用泛型参数的运行时类型调用 IQueryable.OrderBy() 方法?
【发布时间】:2018-08-13 23:48:37
【问题描述】:

我需要使用仅在运行时可用的 TKey 值调用 OrderBy<T, TKey>(Func<T, 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,因此不能与需要Func<T, TKey> 类型参数的OrderBy() 一起使用。但是,TKey 可以是不同的值类型,例如 stringint,它们仅在运行时才知道。这甚至可以做到吗?如果可以,怎么做?

【问题讨论】:

  • 你想达到什么目的?您可以省略这样的通用参数:someList.OrderBy(x => x.MyProperty).
  • 我正在尝试传递 Expression<Func<T, TKey>> 而不是硬编码的 lambda 表达式。
  • 您能否添加一个示例代码,说明您希望如何使用它?
  • 我的项目nuget.org/packages/OrderByString 根据要排序的属性的字符串值在幕后创建您引用的 lambda 表达式。

标签: c# generics reflection


【解决方案1】:

方法MethodInfo.Invoke()用于通过提供的参数进行方法调用,不能用于生成表达式。要生成可用作 .OrderBy() 方法的参数的 lambda Expression,请改用:

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

【讨论】:

    猜你喜欢
    • 2021-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-22
    • 2010-11-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多