我想获得方法System.Linq.Queryable.OrderyBy<T, TKey>(the IQueryable<T> source, Expression<Func<T,TKey>> keySelector)
方法,但我一直想出空值。
var type = typeof(T);
var propertyInfo = type.GetProperty(group.PropertyName);
var propertyType = propertyInfo.PropertyType;
var sorterType = typeof(Func<,>).MakeGenericType(type, propertyType);
var expressionType = typeof(Expression<>).MakeGenericType(sorterType);
var queryType = typeof(IQueryable<T>);
var orderBy = typeof(System.Linq.Queryable).GetMethod("OrderBy", new[] { queryType, expressionType }); /// is always null.
有没有人有任何见解?我宁愿不循环遍历GetMethods
结果。
答案 0 :(得分:21)
解决了(通过黑客攻击LINQ)!
我在研究同样的问题时看到了你的问题。在找不到好的解决方案之后,我有了查看LINQ表达式树的想法。这就是我想出的:
public static MethodInfo GetOrderByMethod<TElement, TSortKey>()
{
Func<TElement, TSortKey> fakeKeySelector = element => default(TSortKey);
Expression<Func<IEnumerable<TElement>, IOrderedEnumerable<TElement>>> lamda
= list => list.OrderBy(fakeKeySelector);
return (lamda.Body as MethodCallExpression).Method;
}
static void Main(string[] args)
{
List<int> ints = new List<int>() { 9, 10, 3 };
MethodInfo mi = GetOrderByMethod<int, string>();
Func<int,string> keySelector = i => i.ToString();
IEnumerable<int> sortedList = mi.Invoke(null, new object[] { ints,
keySelector }
) as IEnumerable<int>;
foreach (int i in sortedList)
{
Console.WriteLine(i);
}
}
输出:10 3 9
编辑:如果你在编译时不知道类型,那么如何获取方法:
public static MethodInfo GetOrderByMethod(Type elementType, Type sortKeyType)
{
MethodInfo mi = typeof(Program).GetMethod("GetOrderByMethod", Type.EmptyTypes);
var getOrderByMethod = mi.MakeGenericMethod(new Type[] { elementType,
sortKeyType });
return getOrderByMethod.Invoke(null, new object[] { }) as MethodInfo;
}
请务必将typeof(Program)替换为typeof(WhateverClassYouDeclareTheseMethodsIn)。
答案 1 :(得分:12)
您的解决方案的一种变体,作为扩展方法:
public static class TypeExtensions
{
private static readonly Func<MethodInfo, IEnumerable<Type>> ParameterTypeProjection =
method => method.GetParameters()
.Select(p => p.ParameterType.GetGenericTypeDefinition());
public static MethodInfo GetGenericMethod(this Type type, string name, params Type[] parameterTypes)
{
return (from method in type.GetMethods()
where method.Name == name
where parameterTypes.SequenceEqual(ParameterTypeProjection(method))
select method).SingleOrDefault();
}
}
答案 2 :(得分:5)
我认为以下扩展方法可以解决问题:
public static MethodInfo GetGenericMethod(
this Type type, string name, Type[] generic_type_args, Type[] param_types, bool complain = true)
{
foreach (MethodInfo m in type.GetMethods())
if (m.Name == name)
{
ParameterInfo[] pa = m.GetParameters();
if (pa.Length == param_types.Length)
{
MethodInfo c = m.MakeGenericMethod(generic_type_args);
if (c.GetParameters().Select(p => p.ParameterType).SequenceEqual(param_types))
return c;
}
}
if (complain)
throw new Exception("Could not find a method matching the signature " + type + "." + name +
"<" + String.Join(", ", generic_type_args.AsEnumerable()) + ">" +
"(" + String.Join(", ", param_types.AsEnumerable()) + ").");
return null;
}
调用类似于(只是更改原始代码的最后一行):
var type = typeof(T);
var propertyInfo = type.GetProperty(group.PropertyName);
var propertyType = propertyInfo.PropertyType;
var sorterType = typeof(Func<,>).MakeGenericType(type, propertyType);
var expressionType = typeof(Expression<>).MakeGenericType(sorterType);
var queryType = typeof(IQueryable<T>);
var orderBy = typeof(Queryable).GetGenericMethod("OrderBy",
new Type[] { type, propertyType },
new[] { queryType, expressionType });
与其他解决方案有什么不同:结果方法与参数类型完全匹配,而不仅仅是它们的通用基类型。
答案 3 :(得分:4)
我不相信有一种简单的方法可以做到这一点 - 它基本上是反射中缺少的功能,IIRC。你必须遍历这些方法才能找到你想要的方法:(
答案 4 :(得分:2)
var orderBy =
(from methodInfo in typeof(System.Linq.Queryable).GetMethods()
where methodInfo.Name == "OrderBy"
let parameterInfo = methodInfo.GetParameters()
where parameterInfo.Length == 2
&& parameterInfo[0].ParameterType.GetGenericTypeDefinition() == typeof(IQueryable<>)
&& parameterInfo[1].ParameterType.GetGenericTypeDefinition() == typeof(Expression<>)
select
methodInfo
).Single();
答案 5 :(得分:1)
使用lambda表达式可以轻松获得通用方法
var method = type.GetGenericMethod
(c => c.Validate((IValidator<object>)this, o, action));
在这里阅读更多相关信息:
http://www.nerdington.com/2010/08/calling-generic-method-without-magic.html
答案 6 :(得分:1)
如果您在编译时知道这些类型,可以使用更少的代码而不使用Expression类型,或者完全依赖Linq,如下所示:
public static MethodInfo GetOrderByMethod<TElement, TSortKey>() {
IEnumerable<TElement> col = null;
return new Func<Func<TElement, TSortKey>, IOrderedEnumerable<TElement>>(col.OrderBy).Method;
}
答案 7 :(得分:0)
我认为它可以像上课一样制作:
public static class SortingUtilities<T, TProperty>
{
public static IOrderedQueryable<T> ApplyOrderBy(IQueryable<T> query, Expression<Func<T, TProperty>> selector)
{
return query.OrderBy(selector);
}
public static IOrderedQueryable<T> ApplyOrderByDescending(IQueryable<T> query, Expression<Func<T, TProperty>> selector)
{
return query.OrderByDescending(selector);
}
public static IQueryable<T> Preload(IQueryable<T> query, Expression<Func<T, TProperty>> selector)
{
return query.Include(selector);
}
}
你甚至可以这样使用它:
public class SortingOption<T> where T: class
{
private MethodInfo ascendingMethod;
private MethodInfo descendingMethod;
private LambdaExpression lambda;
public string Name { get; private set; }
public SortDirection DefaultDirection { get; private set; }
public bool ApplyByDefault { get; private set; }
public SortingOption(PropertyInfo targetProperty, SortableAttribute options)
{
Name = targetProperty.Name;
DefaultDirection = options.Direction;
ApplyByDefault = options.IsDefault;
var utilitiesClass = typeof(SortingUtilities<,>).MakeGenericType(typeof(T), targetProperty.PropertyType);
ascendingMethod = utilitiesClass.GetMethod("ApplyOrderBy", BindingFlags.Static | BindingFlags.Public | BindingFlags.IgnoreCase);
descendingMethod = utilitiesClass.GetMethod("ApplyOrderByDescending", BindingFlags.Static | BindingFlags.Public | BindingFlags.IgnoreCase);
var param = Expression.Parameter(typeof(T));
var getter = Expression.MakeMemberAccess(param, targetProperty);
lambda = Expression.Lambda(typeof(Func<,>).MakeGenericType(typeof(T), targetProperty.PropertyType), getter, param);
}
public IQueryable<T> Apply(IQueryable<T> query, SortDirection? direction = null)
{
var dir = direction.HasValue ? direction.Value : DefaultDirection;
var method = dir == SortDirection.Ascending ? ascendingMethod : descendingMethod;
return (IQueryable<T>)method.Invoke(null, new object[] { query, lambda });
}
}
具有如下属性:
public class SortableAttribute : Attribute
{
public SortDirection Direction { get; set; }
public bool IsDefault { get; set; }
}
和这个枚举:
public enum SortDirection
{
Ascending,
Descending
}
答案 8 :(得分:0)
只是另一条评论(它应该是,但由于它太长了,我必须将其作为答案发布)跟随@NeilWhitaker -s答案(这里使用Enumerable.Count),因为我们正在清除字符串:) 为什么不在你的bytype方法中使用表达式树呢? 类似的东西:
#region Count
/// <summary>
/// gets the
/// public static int Count<TSource>(this IEnumerable<TSource> source);
/// methodinfo
/// </summary>
/// <typeparam name="TSource">type of the elements</typeparam>
/// <returns></returns>
public static MethodInfo GetCountMethod<TSource>()
{
Expression<Func<IEnumerable<TSource>, int>> lamda = list => list.Count();
return (lamda.Body as MethodCallExpression).Method;
}
/// <summary>
/// gets the
/// public static int Count<TSource>(this IEnumerable<TSource> source);
/// methodinfo
/// </summary>
/// <param name="elementType">type of the elements</param>
/// <returns></returns>
public static MethodInfo GetCountMethodByType(Type elementType)
{
// to get the method name, we use lambdas too
Expression<Action> methodNamer = () => GetCountMethod<object>();
var gmi = ((MethodCallExpression)methodNamer.Body).Method.GetGenericMethodDefinition();
var mi = gmi.MakeGenericMethod(new Type[] { elementType });
return mi.Invoke(null, new object[] { }) as MethodInfo;
}
#endregion Disctinct
答案 9 :(得分:0)
今天有一个很好的替代方法 Type.MakeGenericMethodParameter
。以下代码段检索 Queryable.OrderBy
方法:
var TSource = Type.MakeGenericMethodParameter(0);
var TKey = Type.MakeGenericMethodParameter(1);
var orderBy = typeof(Queryable).GetMethod(nameof(Queryable.OrderBy), 2, BindingFlags.Static | BindingFlags.Public, null, CallingConventions.Standard
, new[] { typeof(IQueryable<>).MakeGenericType(TSource), typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(TSource, TKey)) }
, null);
Assert.NotNull(orderBy);