0

The System.Linq.Queryable type has a method named Contains with two overloads:

  • bool Contains<TSource>(this IQueryable<TSource>, TSource)
  • bool Contains<TSource>(this IQueryable<TSource>, TSource, IEqualityComparer<TSource>?)

I want to get the first overload, so I use the following code:

var queryableContainsMethod = typeof(Queryable).GetMethod(
    nameof(Queryable.Contains),
    new[] { typeof(IQueryable<T>), typeof(T), });

But this returns null. Why?


NOTE: I am not asking how to get a reference to this method; I am asking why the code that I expect to give me said reference, does not.

Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
  • I expect the issue is that the method itself is generic. – ProgrammingLlama Jun 29 '23 at 10:30
  • You probably also need to specify the binding flags: `BindingFlags.Static | BindingFlags.Public` – vc 74 Jun 29 '23 at 10:39
  • 1
    @vc74 @PanagiotisKanavos Are the [docs](https://learn.microsoft.com/en-us/dotnet/api/system.type.getmethod?view=net-7.0#system-type-getmethod(system-string-system-type())) wrong? The top of the remarks section for the overload OP is using states: _"The search for `name` is case-sensitive. The search includes public static and public instance methods."_, so I would expect static methods to be included without specifying binding flags. [In testing](https://rextester.com/MSERB51521), that does indeed seem to be the case. – ProgrammingLlama Jun 29 '23 at 10:48
  • 1
    what is `T` in your calling code? – MakePeaceGreatAgain Jun 29 '23 at 10:55
  • Does this answer your question? [How do you call GetMethod for a generic function that takes a generic parameter (without using GetMethods)?](https://stackoverflow.com/questions/29904106/how-do-you-call-getmethod-for-a-generic-function-that-takes-a-generic-parameter) – Charlieface Jun 29 '23 at 10:57
  • @MakePeaceGreatAgain It's from the enclosing type. Which is part of the problem, since if I wasn't inside a generic type I'd have no way to specify `typeof(T)`, and AFAIK in C# there's no way to get a reference to an unbounded generic type (you can get an unbounded reference to a type that _uses_ generics, but that's not the same thing). – Ian Kemp Jun 29 '23 at 12:48

2 Answers2

3

The reason is that the parameters are generic while IQueryable<T> is not, for reflection purposes. It uses the T type of the enclosing method.

Unlike what I assumed, there's no need to specify Static in the binding flags.

It's possible to use typeof on a generic type by omitting the type parameters, eg typeof(IQueryable<>)

Borrowing from this similar question about OrderBy, the following snippet can retrieve the Contains(TSource) overload:

var TSource = Type.MakeGenericMethodParameter(0);

var method=typeof(Queryable).GetMethod(
    nameof(Queryable.Contains),
    new[] { 
        typeof(IQueryable<>).MakeGenericType(TSource), 
        TSource 
    }
);

To get the second, we need to specify the IEqualityComparer<> parameter too:

var TSource = Type.MakeGenericMethodParameter(0);

var method=typeof(Queryable).GetMethod(
    nameof(Queryable.Contains),
    new[] { 
        typeof(IQueryable<>).MakeGenericType(TSource), 
        TSource,
        typeof(IEqualityComparer<>).MakeGenericType(TSource)
    }
);
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • Thank you sir! I really did try searching for this topic, but Google is becoming so cluttered with spam and sites that are just using SO data with different SEO, and the SO search is and always has been useless, thus I resorted to posting this question. – Ian Kemp Jun 29 '23 at 12:52
2

My guess is that it is due to the generic type argument.

T will resolve to a specific type at runtime. So if T is an int it will look for a method with the signature Contains<int>(this IQueryable<int>, int). But no such method exists. You instead need to look for a generic method, without specifying the actual type of the generic method parameter type.

I'm not sure exactly how you should write that, but reflection with generic types tend to be even more complicated than regular reflection. There are a bunch of of reflection methods, like MakeGenericMethod to help convert from or to an open generic type and a type with the generic parameters specified.

As various commenters have noted, specifying binding flags might also be needed, but I would think you also need handle the generic types some other way.

JonasH
  • 28,608
  • 2
  • 10
  • 23