4

I'm trying to get the MethodInfo for Enumerable.SequenceEqual, using Type.GetMethod(...). So far I have tried the following:

var mi = typeof(Enumerable).GetMethod(nameof(Enumerable.SequenceEqual),
    BindingFlags.Static | BindingFlags.Public, null, CallingConventions.Any,
    new Type[] { typeof(IEnumerable<>), typeof(IEnumerable<>) }, null);

and

var enumTyped = typeof(IEnumerable<>).MakeGenericType(ValueType);
var mi = typeof(Enumerable).GetMethod(nameof(Enumerable.SequenceEqual),
    BindingFlags.Static | BindingFlags.Public, null, CallingConventions.Any,
    new Type[] { enumTyped, enumTyped }, null);

However, both solutions return null instead of the method I want. I know the method is retrievable by calling GetMethods() and filtering, but I'd very much like to know how to retrieve it using GetMethod(...).

mycroes
  • 645
  • 8
  • 20
  • Maybe it should be typeof(Enumerable<>) instead of typeof(Enumerable)? – Alex Sep 25 '15 at 07:18
  • 1
    possible duplicate of [Select Right Generic Method with Reflection](http://stackoverflow.com/questions/3631547/select-right-generic-method-with-reflection) – thehennyy Sep 25 '15 at 07:29

3 Answers3

3

Unfortunately, in order to get the generic generic methods using Type.GetMethod(string name, Type[] types) you have to provide the method the right generic types in the Type[], which means that when you try to do this:

Type requiredType = typeof(IEnumerable<>);
typeof(Enumerable).GetMethod("SequenceEqual", new Type[] { requiredType, requiredType });

You actually needed to do something like that:

Type requiredType = typeof(IEnumerable<TSource>);
typeof(Enumerable).GetMethod("SequenceEqual", new Type[] { requiredType, requiredType });

Since if you look at the signature of SequenceEqual, the generic type is IEnumerable<TSource> not IEnumerable<>.

public static IEnumerable<TSource> SequenceEqual<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second);

BUT: you don't have access to the type TSource in order use it. So the only way to get IEnumerable<TSource> is using reflection like the following:

MethodInfo info = typeof(Enumerable)
    .GetMethods(BindingFlags.Static | BindingFlags.Public)
    .Where(x => x.Name.Contains("SequenceEqual"))
    .Single(x => x.GetParameters().Length == 2);
Type genericType = typeof(IEnumerable<>).MakeGenericType(infos.GetGenericArguments());

and than getting the method using

typeof(Enumerable).GetMethod("SequenceEqual", new Type[] { genericType, genericType });

But this requires us to get the SequenceEqual method anyway, so the sad fact is getting the method a generic method when there are few overloads using GetMethod instead of GetMethods is practically impossible* (You CAN implement a Binder and use it in the GetMethod method but it will require very long coding which will possibly be buggy and unmaintainable and should be avoided).

Tamir Vered
  • 10,187
  • 5
  • 45
  • 57
  • 1
    Just additions: if somehow would be possible to get right `TSource` type: `Type type=typeof(TSource);`, then you can simply do that `type.DeclaringMethod` to get right method. – user4003407 Sep 26 '15 at 06:58
2

Want to add to previous answers a bit. First, it's indeed not possible to use single GetMethod to do what you want. But, if you don't want to call GetMethods and get all 180+ methods of Enumerable, you can do this:

var mi = typeof(Enumerable).GetMember(nameof(Enumerable.SequenceEqual), MemberTypes.Method,
            BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod).OfType<MethodInfo>().ToArray();

GetMember call will return you just 2 overloads of SequenceEqual method of which you can choose one and do MakeGenericMethod as shown in other answers. Also, depending on your goal, you may consider to use expressions:

var source = Expression.Parameter(
            typeof(IEnumerable<string>), "source");
var target = Expression.Parameter(
            typeof(IEnumerable<string>), "target");
var callExp = Expression.Call(typeof(Enumerable), "SequenceEqual", new Type[] { typeof(string)},
            source, target);            
var lambda = Expression.Lambda<Func<IEnumerable<string>, IEnumerable<string>, bool>>(callExp, source, target).Compile();
var result = lambda(new[] { "1", "2", "3" }, new[] { "1", "2", "3" });
Debug.Assert(result);
Evk
  • 98,527
  • 8
  • 141
  • 191
  • 1
    Although this is not exactly answering the question, it's exactly what I need. I was actually using the `MethodInfo` to pass into `Expression.Call`, this saves me from creating it first. Would also deserve another +1 just for the `GetMember(...)` explanation. – mycroes Sep 25 '15 at 09:44
0

Another one a bit different answer based on two previous answers but with the MakeGenericMethod call and VisualBasic version.

Dim lSequanceEqual As MethodInfo = GetType(System.Linq.Enumerable).GetMethods()
    .First(Function(mi) mi.Name.Contains(NameOf(System.Linq.Enumerable.SequenceEqual)) AndAlso mi.GetParameters.Length = 2)
Dim lMethod As MethodInfo = lSequanceEqual.MakeGenericMethod(New Type() { GetType(String)}) 
if CBool(lMethod.Invoke(Nothing, New Object() { firstList, secondList})) Then 'Do something
Drreamer
  • 332
  • 1
  • 10