سؤال

I am trying to get return the correct "Where" extension method using reflection, in order to build a custom Expression. I have tried several ways but the closest I get, throws an exception: "An unhandled exception of type 'System.Reflection.AmbiguousMatchException' occurred in mscorlib.dll"

I know this is because there are two Where methods defined in the Enumrable class - but how can I return the Where method which using only just a predicate of

 Func<T, bool>. 

What I have at the moment is:

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

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

I have also tried (this one returns null):

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

and (also returns null)

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

and (this one returns the same Ambiguous Exception)

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

Could anyone help at all please?

Thanks

هل كانت مفيدة؟

المحلول

In my opinion, the current answers, including the accepted one, are far more complicated than necessary. If you have a type T that you can use at compile time, you can get the MethodInfo like so:

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

As an extra bonus, this is strongly typed. It will compile only if the Enumerable.Where can be resolved, as opposed to anything that looks for a string "Where": that would compile just fine if you accidentally type "Wehre" instead, but would fail at runtime.

نصائح أخرى

I'm sure there are easier ways, but here's one:

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

This is a little brittle though (think Microsoft adding more overloads in a future version of .NET). Perhaps more robust (but even more monstrous; surely there must be a better way) is:

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

Having a template type T, this should work:

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

The Where method takes two parameters: the IEnumerable and the comparison function.

I think the easiest way to tell the two overloads apart is:

var whereMethod = typeof(Enumerable).GetMethods()
                .Single(m => m.Name == "Where" && m.GetParameters()[1].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>))
.MakeGenericType(typeof(TSub));
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top