在不使用GetMethods的情况下获取通用方法

我想得到方法System.Linq.Queryable.OrderyBy(the IQueryable source, Expression<Func> 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); var orderBy = typeof(System.Linq.Queryable).GetMethod("OrderBy", new[] { queryType, expressionType }); /// is always null. 

有没有人有任何见解? 我宁愿不循环GetMethods结果。

解决了(通过黑客攻击LINQ)!

我在研究同样的问题时看到了你的问题。 在找不到好的解决方案之后,我有了查看LINQ表达式树的想法。 这就是我想出的:

 public static MethodInfo GetOrderByMethod() { Func fakeKeySelector = element => default(TSortKey); Expression, IOrderedEnumerable>> lamda = list => list.OrderBy(fakeKeySelector); return (lamda.Body as MethodCallExpression).Method; } static void Main(string[] args) { List ints = new List() { 9, 10, 3 }; MethodInfo mi = GetOrderByMethod(); Func keySelector = i => i.ToString(); IEnumerable sortedList = mi.Invoke(null, new object[] { ints, keySelector } ) as IEnumerable; 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(WhateverClassYouDeclareTheseMethodsIn)替换typeof(Program)。

作为扩展方法的解决方案的变体:

 public static class TypeExtensions { private static readonly Func> 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(); } } 

我认为以下扩展方法将是问题的解决方案:

 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); var orderBy = typeof(Queryable).GetGenericMethod("OrderBy", new Type[] { type, propertyType }, new[] { queryType, expressionType }); 

与其他解决方案有什么不同:结果方法与参数类型完全匹配,而不仅仅是它们的通用基类型。

我不相信有一种简单的方法可以做到这一点 – 它基本上是reflection中缺少的function,IIRC。 你必须遍历方法找到你想要的那个:(

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

如果你在编译时知道类型,你可以使用更少的代码而不使用Expression类型,或者根本不依赖于Linq,如下所示:

 public static MethodInfo GetOrderByMethod() { IEnumerable col = null; return new Func, IOrderedEnumerable>(col.OrderBy).Method; } 

使用lambda表达式可以轻松获得generics方法

  var method = type.GetGenericMethod (c => c.Validate((IValidator)this, o, action)); 

在这里阅读更多相关信息:

http://www.nerdington.com/2010/08/calling-generic-method-without-magic.html

http://web.archive.org/web/20100911074123/http://www.nerdington.com/2010/08/calling-generic-method-without-magic.html

我认为它可以像上课一样制作:

 public static class SortingUtilities { public static IOrderedQueryable ApplyOrderBy(IQueryable query, Expression> selector) { return query.OrderBy(selector); } public static IOrderedQueryable ApplyOrderByDescending(IQueryable query, Expression> selector) { return query.OrderByDescending(selector); } public static IQueryable Preload(IQueryable query, Expression> selector) { return query.Include(selector); } } 

你甚至可以这样使用它:

 public class SortingOption 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 Apply(IQueryable query, SortDirection? direction = null) { var dir = direction.HasValue ? direction.Value : DefaultDirection; var method = dir == SortDirection.Ascending ? ascendingMethod : descendingMethod; return (IQueryable)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 } 

只是另一个评论(它应该是,但由于它太长了,我必须将其作为答案发布)跟随@NeilWhitaker -s答案(这里使用Enumerable.Count),因为我们正在清除字符串: )为什么不在你的bytype方法中使用表达式树呢? 就像是 :

  #region Count ///  /// gets the /// public static int Count<TSource>(this IEnumerable<TSource> source); /// methodinfo ///  /// type of the elements ///  public static MethodInfo GetCountMethod() { Expression, int>> lamda = list => list.Count(); return (lamda.Body as MethodCallExpression).Method; } ///  /// gets the /// public static int Count<TSource>(this IEnumerable<TSource> source); /// methodinfo ///  /// type of the elements ///  public static MethodInfo GetCountMethodByType(Type elementType) { // to get the method name, we use lambdas too Expression methodNamer = () => GetCountMethod(); var gmi = ((MethodCallExpression)methodNamer.Body).Method.GetGenericMethodDefinition(); var mi = gmi.MakeGenericMethod(new Type[] { elementType }); return mi.Invoke(null, new object[] { }) as MethodInfo; } #endregion Disctinct