为了账号安全,请及时绑定邮箱和手机立即绑定

选择具有反射的右通用方法

选择具有反射的右通用方法

C#
墨色风雨 2019-08-09 10:30:03
选择具有反射的右通用方法我想通过反射选择正确的通用方法,然后调用它。通常这很容易。例如var method = typeof(MyType).GetMethod("TheMethod");var typedMethod = method.MakeGenericMethod(theTypeToInstantiate);但是,当方法存在不同的泛型重载时,问题就开始了。例如,System.Linq.Queryable类中的静态方法。'Where'方法有两种定义static IQueryable<T> Where(this IQueryable<T> source, Expression<Func<T,bool>> predicate)static IQueryable<T> Where(this IQueryable<T> source, Expression<Func<T,int,bool>> predicate)这说明GetMethod无法正常工作,因为它无法让两者黯然失色。因此,我想选择正确的。到目前为止,我经常只采取第一种或第二种方法,这取决于我的需要。像这样:var method = typeof (Queryable).GetMethods().First(m => m.Name == "Where");var typedMethod = method.MakeGenericMethod(theTypeToInstantiate);但是我对此并不满意,因为我做了一个很大的假设,即第一种方法是正确的。我宁愿通过参数类型找到正确的方法。但我无法弄清楚如何。我尝试传递'类型',但它没有用。        var method = typeof (Queryable).GetMethod(             "Where", BindingFlags.Static,             null,             new Type[] {typeof (IQueryable<T>), typeof (Expression<Func<T, bool>>)},             null);所以有人知道如何通过反射找到'正确'的通用方法。例如,Queryable类的'Where'方法的正确版本?
查看完整描述

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


查看完整回答
反对 回复 2019-08-09
?
慕田峪4524236

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的实例方法示例。


查看完整回答
反对 回复 2019-08-09
  • 3 回答
  • 0 关注
  • 390 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信