referencing desired overloaded generic method
Asked Answered
W

6

14

given

public Class Example
{

public static void Foo< T>(int ID){}

public static void Foo< T,U>(int ID){}

}

Questions:

  1. Is it correct to call this an "overloaded generic method"?
  2. How can either method be specified in creation of a MethodInfo object?

    Type exampleType = Type.GetType("fullyqualifiednameOfExample, namespaceOfExample");
    MethodInfo mi = exampleType.GetMethod("Foo", BindingFlags.Public|BindingFlags.Static, null, new Type[] {typeof(Type), typeof(Type) }, null);
    

argument 4 causes the compiler much displeasure

Wrote answered 25/2, 2009 at 22:12 Comment(1)
You're missing a return type in your method definitions.Nordin
N
15

I can't find a way of using GetMethod that would do what you want. But you can get all the methods and go through the list until you find the method that you want.

Remember you need to call MakeGenericMethod before you can actually use it.

var allMethods = typeof (Example).GetMethods(BindingFlags.Public | BindingFlags.Static);
MethodInfo foundMi = allMethods.FirstOrDefault(
    mi => mi.Name == "Foo" && mi.GetGenericArguments().Count() == 2);
if (foundMi != null)
{
    MethodInfo closedMi = foundMi.MakeGenericMethod(new Type[] {typeof (int), typeof (string)});
    Example example= new Example();
    closedMi.Invoke(example, new object[] { 5 });
}
Nordin answered 25/2, 2009 at 22:29 Comment(0)
N
5

Here are the answers to your questions along with an example:

  1. Yes, although there are two things really to be aware of here with generic methods, type inference and overload method resolution. Type inference occurs at compile time before the compiler tries to resolve overloaded method signatures. The compiler applies type inference logic to all generic methods that share the same name. In the overload resolution step, the compiler includes only those generic methods on which type inference succeeded. More here...

  2. Please see the full example Console Application program code below that shows how several variants of the Foo method can be specified in creation of a MethodInfo object and then invoked using an Extension method:

Program.cs

class Program
{
    static void Main(string[] args)
    {
        MethodInfo foo1 = typeof(Example).GetGenericMethod("Foo",
            new[] { typeof(string) },
            new[] { typeof(int) },
            typeof(void));

        MethodInfo foo2 = typeof(Example).GetGenericMethod("Foo",
            new[] { typeof(string), typeof(int) },
            new[] { typeof(int) },
            typeof(void));

        MethodInfo foo3 = typeof(Example).GetGenericMethod("Foo",
            new[] { typeof(string) },
            new[] { typeof(string) },
            typeof(void));

        MethodInfo foo4 = typeof(Example).GetGenericMethod("Foo",
            new[] { typeof(string), typeof(int) },
            new[] { typeof(int), typeof(string) },
            typeof(string));

        Console.WriteLine(foo1.Invoke(null, new object[] { 1 }));
        Console.WriteLine(foo2.Invoke(null, new object[] { 1 }));
        Console.WriteLine(foo3.Invoke(null, new object[] { "s" }));
        Console.WriteLine(foo4.Invoke(null, new object[] { 1, "s" }));
    }
}

Example.cs:

public class Example
{
    public static void Foo<T>(int ID) { }
    public static void Foo<T, U>(int ID) { }
    public static void Foo<T>(string ID) { }
    public static string Foo<T, U>(int intID, string ID) { return ID; }
}

Extensions.cs:

public static class Extensions
{
    public static MethodInfo GetGenericMethod(this Type t, string name, Type[] genericArgTypes, Type[] argTypes, Type returnType)
    {
        MethodInfo foo1 = (from m in t.GetMethods(BindingFlags.Public | BindingFlags.Static)
                           where m.Name == name &&
                           m.GetGenericArguments().Length == genericArgTypes.Length &&
                           m.GetParameters().Select(pi => pi.ParameterType).SequenceEqual(argTypes) &&
                           m.ReturnType == returnType
                           select m).Single().MakeGenericMethod(genericArgTypes);

        return foo1;
    }
}
Nevertheless answered 26/2, 2009 at 0:37 Comment(1)
I concur, great post. Only tweek I've made using this in my code is to omit the ReturnType completely. If you are looking for an overloaded method then you can't have the same arguments and a different ReturnType anyway. This also means we can find methods that have the ReturnType as a generic too.Ormuz
A
1

Better:

Example attempt to get the correct overload of System.Linq.Enumerable.Select

    private static MethodInfo GetMethod<T>(Expression<Func<T>> expression)
    {
        return ((MethodCallExpression)expression.Body).Method;
    }

    public static void CallSelect()
    {
        MethodInfo definition = GetMethod(() => Enumerable.Select(null, (Func<object, object>)null)).GetGenericMethodDefinition();
        definition.MakeGenericMethod(typeof(int), typeof(int)).Invoke(null, new object[] { new List<int>(), ((Func<int, int>)(x => x)) });
    }
Aye answered 6/2, 2012 at 17:36 Comment(1)
Possibly better, but not the same. Your MethodInfo will have its DeclaringType set to the base class. That is not the case when using reflection. Important detail when tried to read attributes etc.Loire
M
1

Another one liner to find the MethodInfo you want is to create a delegate;

var methodInfo = new Action<int>(Example.Foo<object,object>).Method.GetGenericMethodDefinition();
Marquismarquisate answered 20/2, 2020 at 2:21 Comment(0)
P
0

Here is a Linq one-liner for what you need:

MethodInfo mi = exampleType.GetMethods().First(m=>m.GetGenericArguments().Length == 2);
Perch answered 25/2, 2009 at 22:52 Comment(3)
That's exactly what I have, what's the point in reposting it?Nordin
while Ray identified the correct strategy of sussing out the correct method, thanks for the LINQ one-linnerWrote
Ray: I'm really really sorry about that. I was writting and testing in VS at the same time as you, only, I pressed the submit button after you. I edited the answer to make it less obvious I plagiarized you.Perch
M
0

I make a little modification of your lambda query.

When the parameter type si generic you must make that like that :

I add pi.ParameterType.GetGenericTypeDefinition()

and

(m.ReturnType.IsGenericType ? m.ReturnType.GetGenericTypeDefinition() : m.ReturnType) == returnType)

In this way the method working very fine

MethodInfo foo1 = (from m in t.GetMethods(BindingFlags.Public | BindingFlags.Static)
                         where m.Name == name
                         && m.GetGenericArguments().Length == genericArgTypes.Length
                         && m.GetParameters().Select(pi => pi.ParameterType.IsGenericType ? pi.ParameterType.GetGenericTypeDefinition() : pi.ParameterType).SequenceEqual(argTypes) &&
                         (returnType==null || (m.ReturnType.IsGenericType ? m.ReturnType.GetGenericTypeDefinition() : m.ReturnType) == returnType)
                         select m).FirstOrDefault();
      if (foo1 != null)
      {
        return foo1.MakeGenericMethod(genericArgTypes);
      }
      return null;

Example :

With the modification of the method i can call this extension method

public static IQueryable<T> FilterCulture<T>(this Table<T> t, IDatabaseFilter filter)

With my new Helper like this

var QueryableExpression = MethodInfoHelper.GetGenericMethod(typeof(LinqFilterExtension), "FilterCulture", new Type[] { rowType }, new Type[] { typeof(Table<>), typeof(IDatabaseFilter) }, typeof(IQueryable<>)); 

The signature of my helper is

   public static MethodInfo GetGenericMethod(Type t, string name, Type[] genericArgTypes, Type[] argTypes, Type returnType)
Multilateral answered 22/4, 2009 at 18:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.