如何为“Where”扩展方法获取正确的MethodInfo

时间:2014-03-01 16:37:44

标签: c# expression where methodinfo

我试图使用反射返回正确的“Where”扩展方法,以构建自定义Expression。我尝试了几种方法,但是我得到的最接近,抛出异常: “mscorlib.dll中发生了'System.Reflection.AmbiguousMatchException'类型的未处理异常”

我知道这是因为在Enumrable类中定义了两个Where方法 - 但是如何返回仅使用

谓词的Where方法
 Func<T, bool>. 

目前我所拥有的是:

var collectionType = typeof(TSub);
Type tIEnumerable = typeof(IEnumerable<>).MakeGenericType(collectionType);

MethodInfo methodInfo =
        typeof(Enumerable)
        .GetMethod("Where")                
        .MakeGenericMethod(collectionType);

我也尝试过(这个返回null):

MethodInfo methodWhere = typeof(Enumerable).GetMethod("Where", new[] { typeof(TSub )});

和(也返回null)

 MethodInfo methodWhere = typeof(Enumerable).GetMethod("Where", new[] { collectionType })

和(这个返回相同的模糊异常)

MethodInfo methodWhere = typeof(Enumerable).GetMethod("Where", BindingFlags.Public |      BindingFlags.Static)

有人可以帮忙吗?

由于

4 个答案:

答案 0 :(得分:1)

我确信有更简单的方法,但这里有一个:

typeof(Enumerable).GetMethods()
                  .Single(method => method.Name == "Where" 
                          && method.GetParameters()
                                   .ElementAt(1)
                                   .ParameterType
                                   .GetGenericTypeDefinition() == typeof(Func<,>)) 
                  .MakeGenericMethod(typeof(TSub))

虽然这有点脆弱(想想微软在未来的.NET版本中添加了更多的重载)。也许更强大(但更怪异;肯定必须有更好的方法)是:

var methods = from method in typeof(Enumerable).GetMember("Where")
                                               .OfType<MethodInfo>()                               
              let typeArgs = method.GetGenericArguments()
              where typeArgs.Length == 1
              let typeArg = typeArgs.Single()
              where !typeArg.GetGenericParameterConstraints().Any()
              let seqtype = typeof(IEnumerable<>).MakeGenericType(typeArg)                    
              where method.ReturnType == seqtype
              let expectedParams = new[]
              {
                 seqtype, 
                 typeof(Func<,>).MakeGenericType(typeArg, typeof(bool))
              }
              where method.GetParameters()
                          .Select(parameter => parameter.ParameterType)
                          .SequenceEqual(expectedParams)

              select method.MakeGenericMethod(typeof(TSub));


var result = methods.Single();

答案 1 :(得分:1)

在我看来,目前的答案,包括已接受的答案,远比必要的复杂得多。如果你有一个可以在编译时使用的类型T,你可以得到MethodInfo,如下所示:

Func<IEnumerable<T>, Func<T, bool>, IEnumerable<T>> whereDelegate = Enumerable.Where;
MethodInfo whereMethodInfo = whereDelegate.Method;

作为额外奖励,这是强类型。如果Enumerable.Where可以被解析,它将只编译 ,而不是寻找字符串"Where"的任何东西:如果你不小心输入"Wehre",它就会很好地编译相反,但在运行时会失败。

答案 2 :(得分:0)

我认为分开两个重载的最简单方法是:

var whereMethod = typeof(Enumerable).GetMethods()
                .Single(m => m.Name == "Where" && m.GetParameters()[1].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>))
.MakeGenericType(typeof(TSub));

答案 3 :(得分:0)

模板类型为T,这应该有效:

var methodWhere = typeof(Enumerable).GetMethod("Where", BindingFlags.Public |    BindingFlags.Static, new Type[]{ typeof(IEnumerable<T>), typeof(Func<T, bool>) });

Where方法有两个参数:IEnumerable和比较函数。