I want to get the method System.Linq.Queryable.OrderyBy(the IQueryable source, Expression> keySelector) mehthod, but I keep coming up with nulls.
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.
Does anyone have any insight? I would prefer to not loop through the GetMethods result.
-
I don't believe there's an easy way of doing this - it's basically a missing feature from reflection, IIRC. You have to loop through the methods to find the one you want :(
-
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();
-
A variant of your solution, as an extension method:
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(); } }
Dave : Interesting, thanks I will need to absorb this SquenceEqual method. -
Solved (by hacking LINQ)!
I saw your question while researching the same problem. After finding no good solution, I had the idea to look at the LINQ expression tree. Here's what I came up with:
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); } }
output: 10 3 9
EDIT: Here is how to get the method if you don't know the type at compile-time:
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; }
Be sure to replace typeof(Program) with typeof(WhateverClassYouDeclareTheseMethodsIn).
Dave : Ooooh, very wise. :) -
Using lambda expressions you can get the generic method easily
var method = type.GetGenericMethod (c => c.Validate((IValidator<object>)this, o, action));
Read more about it here:
http://www.nerdington.com/2010/08/calling-generic-method-without-magic.html
-
Hi there,
I think the following extension method would be a solution to the problem:
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; }
The call would be something like (just changing the last line of your original code):
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).GetGenericMethod("OrderBy", new Type[] { type, propertyType }, new[] { queryType, expressionType });
What is different to the other solutions: the resulting method matches the parameter types exactly, not only their generic base types.
0 comments:
Post a Comment