3 回答
TA贡献1796条经验 获得超10个赞
它可以做到,但它不漂亮!
例如,要获得Where
问题中提到的第一个重载,您可以这样做:
var where1 = typeof(Queryable).GetMethods() .Where(x => x.Name == "Where") .Select(x => new { M = x, P = x.GetParameters() }) .Where(x => x.P.Length == 2 && x.P[0].ParameterType.IsGenericType && x.P[0].ParameterType.GetGenericTypeDefinition() == typeof(IQueryable<>) && x.P[1].ParameterType.IsGenericType && x.P[1].ParameterType.GetGenericTypeDefinition() == typeof(Expression<>)) .Select(x => new { x.M, A = x.P[1].ParameterType.GetGenericArguments() }) .Where(x => x.A[0].IsGenericType && x.A[0].GetGenericTypeDefinition() == typeof(Func<,>)) .Select(x => new { x.M, A = x.A[0].GetGenericArguments() }) .Where(x => x.A[0].IsGenericParameter && x.A[1] == typeof(bool)) .Select(x => x.M) .SingleOrDefault();
或者如果你想要第二次重载:
var where2 = typeof(Queryable).GetMethods() .Where(x => x.Name == "Where") .Select(x => new { M = x, P = x.GetParameters() }) .Where(x => x.P.Length == 2 && x.P[0].ParameterType.IsGenericType && x.P[0].ParameterType.GetGenericTypeDefinition() == typeof(IQueryable<>) && x.P[1].ParameterType.IsGenericType && x.P[1].ParameterType.GetGenericTypeDefinition() == typeof(Expression<>)) .Select(x => new { x.M, A = x.P[1].ParameterType.GetGenericArguments() }) .Where(x => x.A[0].IsGenericType && x.A[0].GetGenericTypeDefinition() == typeof(Func<,,>)) .Select(x => new { x.M, A = x.A[0].GetGenericArguments() }) .Where(x => x.A[0].IsGenericParameter && x.A[1] == typeof(int) && x.A[2] == typeof(bool)) .Select(x => x.M) .SingleOrDefault();
TA贡献1875条经验 获得超5个赞
您可以在编译时优雅地选择方法的特定泛型重载,而不将任何字符串传递给运行时搜索,就像这里的其他答案一样。
静态方法
假设您有多个同名的静态方法,例如:
public static void DoSomething<TModel>(TModel model)public static void DoSomething<TViewModel, TModel>(TViewModel viewModel, TModel model)// etc
如果您创建的Action或Func与您要查找的重载的泛型计数和参数计数相匹配,则可以在编译时使用相对较少的杂技来选择它。
示例:选择第一个方法 - 返回void,因此使用Action,取一个泛型。我们使用object来避免指定类型:
var method = new Action<object>(MyClass.DoSomething<object>);
示例:选择第二个方法 - 返回void,因此Action,2个泛型类型因此使用type对象两次,对于2个通用参数中的每一个使用一次:
var method = new Action<object, object>(MyClass.DoSomething<object, object>);
你只需要你想要的方法,而不需要做任何疯狂的管道,也不需要运行时搜索或使用有风险的字符串。
MethodInfo的
通常在Reflection中,您需要MethodInfo对象,您也可以以编译安全的方式获取该对象。这是当您传递要在方法中使用的实际泛型类型时。假设您想要上面的第二种方法:
var methodInfo = method.Method.MakeGenericMethod(type1, type2);
你的泛型方法没有任何反射搜索或调用GetMethod()或脆弱的字符串。
静态扩展方法
你引用Queryable.Where重载的具体例子迫使你在Func定义中有点花哨,但通常遵循相同的模式。最常用的Where()扩展方法的签名是:
public static IQueryable<TModel> Where<TModel>(this IQueryable<TModel>, Expression<Func<TModel, bool>>)
显然这会稍微复杂一点 - 这里是:
var method = new Func<IQueryable<object>, Expression<Func<object, bool>>, IQueryable<object>>(Queryable.Where<object>);var methodInfo = method.Method.MakeGenericMethod(modelType);
实例方法
结合Valerie的评论 - 要获得实例方法,您需要做一些非常相似的事情。假设您的类中有此实例方法:
public void MyMethod<T1>(T1 thing)
首先选择与静态相同的方法:
var method = new Action<object>(MyMethod<object>);
然后调用GetGenericMethodDefinition()
以获取泛型MethodInfo,最后传递您的类型MakeGenericMethod()
:
var methodInfo = method.Method.GetGenericMethodDefinition().MakeGenericMethod(type1);
解耦MethodInfo和参数类型
在问题中没有要求这样做,但是一旦你做了上述事情,你可能会发现自己在一个地方选择了这个方法,并决定在另一个地方传递它的类型。你可以解开这两个步骤。
如果您不确定要传入的泛型类型参数,则始终可以在不使用它们的情况下获取MethodInfo对象。
静态的:
var methodInfo = method.Method;
例如:
var methodInfo = method.Method.GetGenericMethodDefinition();
并将其传递给其他一些知道它想要实例化的类型的方法并调用方法 - 例如:
processCollection(methodInfo, type2);...protected void processCollection(MethodInfo method, Type type2){ var type1 = typeof(MyDataClass); object output = method.MakeGenericMethod(type1, type2).Invoke(null, new object[] { collection });}
这特别有用的一件事是从类内部选择一个类的特定实例方法,然后将其暴露给以后需要它的各种类型的外部调用者。
编辑:清理解释,合并了Valerie的实例方法示例。
- 3 回答
- 0 关注
- 390 浏览
添加回答
举报