pjs
pjs

Reputation: 2731

How do you call GetMethod for a generic function that takes a generic parameter (without using GetMethods)?

I know I can fetch the method info using GetMethods, but I want to know how to do it properly without GetMethods. I have read other SO questions and answers that suggest this is not possible, or suggest just using LINQ instead, but that isn't really an answer to the question.

Consider at the most basic level, a static generic function that takes a single generic parameter.

private static void Test<T>(T val)
{
}

To fetch this method info we can just call Type.GetMethod("Test", BindingFlags.Static | BindingFlags.NonPublic). However if there were some reason we could not use this simple GetMethod signature (perhaps due to multiple overloads), then we need to supply the parameter types. The problem is that I cannot create a parameter type that accurately matches the T val parameter. What's interesting is that I can take the parameters from the method info (fetched with GetMethods) and pass that into GetMethod to get the desired outcome. This means that if it were only possible to create the appropriate generic types (with IsGenericParameter set to true) then I feel like this would be completely possible.

So that means that this is entirely possible in .NET, and only require the creation of the proper type instances. How does one create these type instances? And if they are not possible to create, why aren't they?

I created a simple fiddle to showcase the issue.

Upvotes: 6

Views: 2642

Answers (4)

Mukul Dhiman
Mukul Dhiman

Reputation: 1

A bit late, but for future reference the following also works with .NET core

var flags = BindingFlags.Static | BindingFlags.NonPublic;
//the 0 indicates the index of the parameter
var paramTypes1 = new[] { Type.MakeGenericMethodParameter(0) };

var method = typeof(Program).GetMethod("Test", 1, flags, null, paramTypes1, null);

or simply

var method = typeof(Program).GetMethod("Test", 1,  paramTypes1);

Suppose the method signature was

Test<T>(string val)

Then the paramTypes1 changes to

var paramTypes1 = new[] {typeof(string), Type.MakeGenericMethodParameter(0) };

even if the generic isn't a parameter per say it still needs to be mentioned in the array

Upvotes: 0

Marc Gravell
Marc Gravell

Reputation: 1064114

It isn't readily available, because the types you need are actually generic type arguments that only exist in the method / parameter definition. For example, in your Test<T>(T val), the parameter type is "the T as defined by Test<T>. You can't construct that, because it isn't composed from anything. The only way to obtain that T is via GetParameters().

Basically, that leaves: the hard way - i.e. manually. For example:

var method1 = typeof(Program).GetMethods(flags).Single(x => x.Name == "Test"
     && x.IsGenericMethodDefinition && x.GetParameters().Length == 1
      && x.GetParameters()[0].ParameterType == x.GetGenericArguments()[0])
    .MakeGenericMethod(paramTypes1);

Obviously it is simpler if you know there is only one Test(...) method:

var method = typeof(Program).GetMethod("Test", flags)
    .MakeGenericMethod(paramTypes1);

Upvotes: 8

Grax32
Grax32

Reputation: 4069

This isn't calling GetMethod but the "other" way to get a generic method definition is to "cast" a method group to a specific type, then call .Method.GetGenericDefinition() on it.

In your example, the method signature you need is Action<object> where object is just a placeholder and can be any type that matches the constraints of your generic method.

var genericMethodDefinition =
    ((Action<object>)Test<object>).Method.GetGenericMethodDefinition();

You might select a different overload of "Test" that is defined as private static T Test<T>(T val, int counter) by using the following.

var genericMethodDefinition2 =
    ((Func<object, int, object>)Test<object>).Method.GetGenericMethodDefinition();

Upvotes: 4

user2160375
user2160375

Reputation:

Well, I don't think that it is possible. But other approach (still hard way, I believe that GetMethods would be better):

var method1 =
    typeof (Program).GetMember("Test*",
                               BindingFlags.InvokeMethod |
                               BindingFlags.NonPublic | 
                               BindingFlags.Static)
                    .Cast<MethodInfo>()
                    .Single(
                            m =>
                            m.GetGenericArguments().Length == 1 &&
                            m.GetGenericArguments()[0].IsGenericParameter)
                    .MakeGenericMethod(paramTypes1);

Of course, you can omit MakeGenericMethod to get exacly the same result as method2.

Upvotes: 1

Related Questions