with property parameter multiple method has generic create attribute c# .net generics reflection

parameter - generic t property c#



Obtenga un método genérico sin usar GetMethods (9)

Deseo obtener el método System.Linq.Queryable.OrderyBy<T, TKey>(the IQueryable<T> source, Expression<Func<T,TKey>> keySelector) el método System.Linq.Queryable.OrderyBy<T, TKey>(the IQueryable<T> source, Expression<Func<T,TKey>> keySelector) , pero sigo System.Linq.Queryable.OrderyBy<T, TKey>(the IQueryable<T> source, Expression<Func<T,TKey>> keySelector) nulos.

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.

¿Alguien tiene alguna idea? Preferiría no recorrer el resultado de GetMethods .


Creo que el siguiente método de extensión sería una solución al problema:

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; }

La llamada sería algo así como (simplemente cambiando la última línea de tu código original):

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

Lo que es diferente de las otras soluciones: el método resultante coincide exactamente con los tipos de parámetros, no solo sus tipos básicos genéricos.


Creo que se puede hacer con clase así:

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

Y puedes usar esto incluso así:

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

con un atributo como este:

public class SortableAttribute : Attribute { public SortDirection Direction { get; set; } public bool IsDefault { get; set; } }

y esta enum:

public enum SortDirection { Ascending, Descending }


No creo que haya una manera fácil de hacer esto: básicamente es una característica que falta de reflexión, IIRC. Tienes que recorrer los métodos para encontrar el que quieres :(


Resuelto (pirateando LINQ)!

Vi tu pregunta mientras investigaba el mismo problema. Después de no encontrar una buena solución, tuve la idea de mirar el árbol de expresiones LINQ. Esto es lo que se me ocurrió:

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

salida: 10 3 9

EDITAR: Aquí se muestra cómo obtener el método si no se conoce el tipo en tiempo de compilación:

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; }

Asegúrese de reemplazar typeof (Program) con typeof (WhateverClassYouDeclareTheseMethodsIn).


Si conoce los tipos en tiempo de compilación, puede hacer esto con menos código sin usar el tipo de Expresión, o dependiendo de Linq en absoluto, así:

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


Solo otro comentario (debería ser, pero como es demasiado largo, tengo que publicarlo como respuesta) siguiendo la respuesta de @NeilWhitaker -s (aquí usando Enumerable.Count), ya que estamos en el medio de borrar las cadenas: ) ¿por qué no utilizar los árboles de Expresión en tu método bytype también? Algo como :

#region Count /// <summary> /// gets the /// public static int Count&lt;TSource>(this IEnumerable&lt;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&lt;TSource>(this IEnumerable&lt;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


Una variante de su solución, como método de extensión:

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



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