C# 不使用 GetMethods 获取泛型方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/269578/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Get a generic method without using GetMethods
提问by Dave
I want to get the method System.Linq.Queryable.OrderyBy<T, TKey>(the IQueryable<T> source, Expression<Func<T,TKey>> keySelector)
method, but I keep coming up with nulls.
我想获得方法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.
Does anyone have any insight? I would prefer to not loop through the GetMethods
result.
有没有人有任何见解?我宁愿不遍历GetMethods
结果。
采纳答案by Neil
Solved (by hacking LINQ)!
已解决(通过破解 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:
我在研究同样的问题时看到了你的问题。在没有找到好的解决方案后,我有了查看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);
}
}
output: 10 3 9
输出: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).
请务必将 typeof(Program) 替换为 typeof(WhateverClassYouDeclareThisMethodsIn)。
回答by Jon Skeet
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 :(
我不相信有一种简单的方法可以做到这一点 - 它基本上是反射,IIRC 缺少的功能。您必须遍历这些方法才能找到您想要的方法:(
回答by Dave
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();
回答by Jon Skeet
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();
}
}
回答by Kyle
Using lambda expressions you can get the generic method easily
使用 lambda 表达式,您可以轻松获得泛型方法
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
http://www.nerdington.com/2010/08/calling-generic-method-without-magic.html
回答by qube
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<T>);
var orderBy = typeof(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.
与其他解决方案的不同之处在于:结果方法与参数类型完全匹配,而不仅仅是它们的通用基类型。
回答by Konstantin Isaev
I think that it mabe be made with class like so:
我认为它可以用这样的类来制作:
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);
}
}
And you can use this even like so:
你甚至可以像这样使用它:
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 });
}
}
with attribute like this:
具有这样的属性:
public class SortableAttribute : Attribute
{
public SortDirection Direction { get; set; }
public bool IsDefault { get; set; }
}
and this enum:
和这个枚举:
public enum SortDirection
{
Ascending,
Descending
}
回答by PaulWh
If you do know the types at compile time, you can do this with less code without using the Expression type, or depending on Linq at all, like so:
如果您在编译时确实知道类型,则可以使用更少的代码完成此操作,而无需使用 Expression 类型,或者完全依赖于 Linq,如下所示:
public static MethodInfo GetOrderByMethod<TElement, TSortKey>() {
IEnumerable<TElement> col = null;
return new Func<Func<TElement, TSortKey>, IOrderedEnumerable<TElement>>(col.OrderBy).Method;
}
回答by MBoros
Just another comment (it should be, but since its too long, i have to post it as an answer) following up @NeilWhitaker -s answer (here using Enumerable.Count), since we are in the middle of clearing the strings out :) why not use the Expression trees in your bytype method too? Something like :
只是另一个评论(应该是,但由于它太长了,我必须将其作为答案发布)跟进@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