0

Specifically, I am looking at a bunch of extension methods which are all overloaded like this (they're part of SignalR, if you couldn't guess):

public static IDisposable On(this IHubProxy proxy, string eventName, Action onData);
public static IDisposable On<T>(this IHubProxy proxy, string eventName, Action<T> onData);
public static IDisposable On<T1, T2>(this IHubProxy proxy, string eventName, Action<T1, T2> onData);

Now I have no problem getting the methodInfo for the first (non-generic) On method by doing this:

var methodInfo = typeof(HubProxyExtensions).GetMethod("On", new[] {typeof(IHubProxy), typeof(string), typeof(Action)});

However, I want to be able to get the second or third definition of the "On" method. However, I've found that something like this does not work:

var methodInfo = typeof(HubProxyExtensions).GetMethod("On", new[] {typeof(IHubProxy), typeof(string), typeof(Action<>)});

In the above case, methodInfo ends up being null. Any ideas?

erikjw
  • 45
  • 9
  • It doesn't work because you look for `Action<>`, while the parameter is of type `Action`. You need a custom `Binder`. – IS4 Oct 01 '15 at 15:35

3 Answers3

2

Elaborating a bit on my comment, I've made this custom binder:

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

public class GenericDefinitionBinder : Binder
{
    public static readonly GenericDefinitionBinder Default = new GenericDefinitionBinder();

    public override PropertyInfo SelectProperty(BindingFlags bindingAttr, PropertyInfo[] match, Type returnType, Type[] indexes, ParameterModifier[] modifiers)
    {
        throw new NotImplementedException();
    }

    public override MethodBase SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers)
    {
        return match.SingleOrDefault(m => MethodOkay(m, types));
    }

    private static bool MethodOkay(MethodBase method, Type[] types)
    {
        var pars = method.GetParameters();
        if(types.Length != pars.Length) return false;
        for(int i = 0; i < types.Length; i++)
        {
            var par = pars[i].ParameterType;
            if(!(par == types[i] || (par.IsGenericType && par.GetGenericTypeDefinition() == types[i])))
            {
                return false;
            }
        }
        return true;
    }

    public override void ReorderArgumentArray(ref object[] args, object state)
    {
        throw new NotImplementedException();
    }

    public override object ChangeType(object value, Type type, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    public override MethodBase BindToMethod(BindingFlags bindingAttr, MethodBase[] match, ref object[] args, ParameterModifier[] modifiers, System.Globalization.CultureInfo culture, string[] names, out object state)
    {
        throw new NotImplementedException();
    }

    public override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] match, object value, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Simple use:

var methodInfo = typeof(HubProxyExtensions).GetMethod("On", BindingFlags.Public | BindingFlags.Static, GenericDefinitionBinder.Default, new[] {typeof(IHubProxy), typeof(string), typeof(Action<>)}, null);
IS4
  • 11,945
  • 2
  • 47
  • 86
0

I don't think that there is a way to do this with the current method that you are using. However, by getting all of the methods and then using LINQ you could find that method like this:

var methodInfo = typeof(HubProxyExtensions).GetMethods().FirstOrDefault(x => x.Name == "On"
    && x.GetParameters().Count() == 3
    && x.GetParameters()[0].ParameterType == typeof(IHubProxy)
    && x.GetParameters()[1].ParameterType == typeof(string)
    && x.GetParameters()[2].ParameterType.IsGenericType
    && x.GetParameters()[2].ParameterType.GetGenericTypeDefinition() == typeof(Action<>));

(I know there is lots of repetition of GetParameters() but you get the idea)

TomDoesCode
  • 3,580
  • 2
  • 18
  • 34
0

To get MethodInfo of the second method, you need to call MakeGenericMethod:

MethodInfo method = typeof(HubProxyExtensions).GetMethod("On");
MethodInfo generic = method.MakeGenericMethod(typeof(string));

and set the same T for Action<T> input parameter to invoke:

var onData = new Action<string>(Target);    
generic.Invoke(this, new object[] { proxy, eventName, onData });

UPDATE: Sorry, works only for single On<T> method in class without first (On) or third (On<T1, T2>) method.

Ilya Chumakov
  • 23,161
  • 9
  • 86
  • 114