Ok, so I just coded this, which is essentially a manual type inference, and I think should do what I need.
public static class TypeExtensions {
public static Type GetTypeDefinition ( this Type type ) {
return type.IsGenericType ? type.GetGenericTypeDefinition () : type;
}
public static IEnumerable<Type> GetImproperComposingTypes ( this Type type ) {
yield return type.GetTypeDefinition ();
if ( type.IsGenericType ) {
foreach ( var argumentType in type.GetGenericArguments () ) {
foreach ( var t in argumentType.GetImproperComposingTypes () ) yield return t;
}
}
}
private static Dictionary<Type , Type> GetInferenceMap ( ParameterInfo[] parameters , Type[] types ) {
var genericArgumentsMap = new Dictionary<Type , Type> ();
var match = parameters.All ( parameter => parameter.ParameterType.GetImproperComposingTypes ().Zip ( types[parameter.Position].GetImproperComposingTypes () ).All ( a => {
if ( !a.Item1.IsGenericParameter ) return a.Item1 == a.Item2;
if ( genericArgumentsMap.ContainsKey ( a.Item1 ) ) return genericArgumentsMap[a.Item1] == a.Item2;
genericArgumentsMap[a.Item1] = a.Item2;
return true;
} ) );
return match ? genericArgumentsMap : null;
}
public static MethodInfo MakeGenericMethod ( this Type type , string name , Type[] types ) {
var methods = from method in type.GetMethods ()
where method.Name == name
let parameters = method.GetParameters ()
where parameters.Length == types.Length
let genericArgumentsMap = GetInferenceMap ( parameters , types )
where genericArgumentsMap != null
where method.GetGenericArguments ().Length == genericArgumentsMap.Keys.Count ()
select new {
method ,
genericArgumentsMap
};
return methods.Select ( m => m.method.IsGenericMethodDefinition ? m.method.MakeGenericMethod ( m.method.GetGenericArguments ().Map ( m.genericArgumentsMap ).ToArray () ) : m.method ).SingleOrDefault ();
}
}
So given
public class Foos {
public void Foo<T1 , T2 , T3> ( int a , T1 b , IEnumerable<T2> c , Expression<Func<T1 , T3 , string>> d ) {
}
public void Foo<T1 , T2 , T3> ( int a , T1 b , IEnumerable<T2> c , Expression<Func<T1 , T3 , int>> d ) {
}
public void Foo () {
}
public void Foo ( string s ) {
}
}
I can pick the method I want:
var method = typeof ( Foos ).MakeGenericMethod ( "Foo" , new[] { typeof ( int ) , typeof ( DateTime ) , typeof ( IEnumerable<string> ) , typeof ( Expression<Func<DateTime , double , int>> ) } );
method.Invoke ( new Foos () , new object[] { 1 , DateTime.Now , null , null } );
var method = typeof ( Foos ).MakeGenericMethod ( "Foo" , Type.EmptyTypes );
method.Invoke ( new Foos () , new object[] { } );
var method = typeof ( Foos ).MakeGenericMethod ( "Foo" , new[] { typeof ( string ) } );
method.Invoke ( new Foos () , new object[] { "zozo" } );
This seem to support non-generic methods, but doesn't support explicit generic arguments (obviously), and maybe needs some work, but that's the core of it.