3

Here's my class:

public class MyClass<T>
{
    public void MyMethod(T a)
    {
    }

    public void MyMethod(int a)
    {
    }
}

How would I Invoke MyMethod(T) using Reflection even if int is the generic type parameter for MyClass?

Here's a single statement that does it (Might be inefficient, but I like brevity):

var mc = new MyClass<int>();

typeof(MyClass<int>).GetMethods().ElementAt(
    typeof(MyClass<int>).
    GetGenericTypeDefinition().
    GetMethods().ToList().FindIndex(m =>
        m.Name.Equals("MyMethod") && 
        m.GetParameters().Count() == 1 &&
        m.GetParameters()[0].ParameterType.IsGenericParameter)
    ).Invoke(mc, new object[] { 1 });
Jesus is Lord
  • 14,971
  • 11
  • 66
  • 97
  • 2
    http://stackoverflow.com/questions/4738826/how-to-determine-if-parameterinfo-is-of-generic-type "parameterinfo generic type" –  Nov 04 '12 at 02:52
  • `GetMethods()[0].GetParameters()[0].ParameterType.IsGenericParameter` and `GetMethods()[1].GetParameters()[0].ParameterType.IsGenericParameter` both return false for me. – Jesus is Lord Nov 04 '12 at 02:56

2 Answers2

4

Revised Answer

Okay, so I think I've figured out why the IsGenericParameter property evaluates as false is because you were creating the MyClass type with an explicit type for <T>.

Since the compiler new what type the a parameter was (inferring from the instantiation of the class), I'm guessing that the compiler was treating the parameter as a non-generic type.

Also, based upon what I was reading in MSDN, I think that the ParameterType.IsGenericParameter and Type.IsGenericType property would only evaluate to true whenever you had a method like MyMethod<T>() vs. MyMethod(T a), where the type for <T> is inferred from the type instantiated with the class.

Here's a little program that demonstrates this:

using System;
using System.Linq;
using System.Reflection;

namespace GenericParametersViaReflectionTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // Note: we're using the type without specifying a type for <T>.
            var classType = typeof(MyClass<>);

            foreach (MethodInfo method in classType.GetMembers()
                .Where(method => method.Name == "MyMethod"))
            {
                // Iterate through each parameter of the method
                foreach (var param in method.GetParameters())
                {
                    // For generic methods, the name will be "T" and the FullName
                    // will be null; you can use which ever check you prefer.
                    if (param.ParameterType.Name == "T"
                        || param.ParameterType.FullName == null)
                        Console.WriteLine("We found our generic method!");
                    else
                        Console.WriteLine("We found the non-generic method:");

                    Console.WriteLine("Method Name: {0}", method.Name);
                    Console.WriteLine("Parameter Name: {0}", param.Name);
                    Console.WriteLine("Type: {0}", param.ParameterType.Name);
                    Console.WriteLine("Type Full Name: {0}",
                        param.ParameterType.FullName ?? "null");
                    Console.WriteLine("");
                }   
            }
            Console.Read();
        }
    }
    public class MyClass<T>
    {
        public void MyMethod(T a) { }
        public void MyMethod(int a) { }
    }
}

And the results we end up with are:

We found or generic method!
Method Name: MyMethod
Parameter Name: a
Type: T
Type Full Name: null

We found the non-generic method:
Method Name: MyMethod
Parameter Name: a
Type: Int32
Type Full Name: System.Int32

If you need to create an instance of the class using a particular type, you might find the Activator.CreateInstance class useful too.


Original Answer

I think that if you pass in a parameter of the same datatype that matched one of the explictly set methods (e.g. the Int32 method), then the compiler would automatically select that over the one that accepts a generic parameter. Otherwise, the generic method would be selected by the compiler.

However, if you want to be able to control which method is selected, you could modify each method to have different parameter names, while maintaining identical signatures, like so:

public class MyClass<T>
{
    public void MyMethod(T genericA) {}
    public void MyMethod(int intA) {}
}

Then, using named parameters, you could explicitly call the desired method, like so:

var foo = new MyClass<int>();
foo.MyMethod(genericA: 24); // This will call the "MyMethod" that only accepts <T>.
foo.MyMethod(intA: 19); // This will call the "MyMethod" that only accepts integers.

Edit

For some reason, in my original answer I missed the part where you mentioned using reflection, but it looks my original answer could be coupled with these other answers to give you a viable solution:

Community
  • 1
  • 1
Alexander
  • 2,320
  • 2
  • 25
  • 33
  • 1
    This is true, but .. via reflection now :) –  Nov 04 '12 at 03:15
  • That's true. However I'm confined to reflection because I'm working with plugins and multiple AppDomain's and Ninject and craziness. – Jesus is Lord Nov 04 '12 at 03:17
  • I think this answer http://stackoverflow.com/a/214106/1796930 provides a nice extension method that should do the trick. – Alexander Nov 04 '12 at 03:20
  • 1
    @user1796930: The parameter names are not for me to choose. There could be a naming collision, as in my original code :/ – Jesus is Lord Nov 04 '12 at 03:25
  • 1
    Thanks for the revision -- I appreciate the time you spent on this -- Mike has a simple answer too -- I think he answered first, so I accepted his, but I also put a single statement that does it in my question to help future visitors. – Jesus is Lord Nov 04 '12 at 06:06
3

The only way I was able to do this was to use GetGenericTypeDefinition with IsGenericParameter. In the generic type definition, one method will have IsGenericParameter set to true on the parameter. However, for closed types none of the parameters will have this as true. Then, you can't use the MethodInfo from the generic type definition to invoke the method, so I stored the index and used that to lookup the corresponding MethodInfo in the closed type.

public class Program
{
    public static void Main(string[] args)
    {
        bool invokeGeneric = true;
        MyClass<int> b = new MyClass<int>();
        var type = b.GetType().GetGenericTypeDefinition();
        int index = 0;
        foreach(var mi in type.GetMethods().Where(mi => mi.Name == "MyMethod"))
        {
            if (mi.GetParameters()[0].ParameterType.IsGenericParameter == invokeGeneric)
            {
                break;
            }
            index++;
        }

        var method = b.GetType().GetMethods().Where(mi => mi.Name == "MyMethod").ElementAt(index);
        method.Invoke(b, new object[] { 1 });
    }
}

public class MyClass<T>
{
    public void MyMethod(T a)
    {
        Console.WriteLine("In generic method");
    }

    public void MyMethod(int a)
    {
        Console.WriteLine("In non-generic method");
    }
}
Mike Zboray
  • 39,828
  • 3
  • 90
  • 122