我有一个包含方法重载的类:
public static void Foo(string a, string b)
public static void Foo(DateTime a, DateTime b)
public static void Foo<T>(ICollection<T> a, T b)
public static void Foo<T>(T a, ICollection<T> b)
public static void Foo<T>(ICollection<T> a, ICollection<T> b)
public static void Foo<T, U>(T a, U b) where T : IComparable
public static void Foo<T, U>(T a, U b)
我在运行时以这种方式检索所有方法:
var methods = typeof(MyClass).GetMethods(BindingFlags.Public | BindingFlags.Static);
对于两个给定的参数类型(例如:typeof(int)
,typeof(List<int>)
),我想动态选择最合适的方法。
到目前为止我尝试/检查的内容:
Binder.SelectMethod()
:该方法不支持泛型。 typeof().IsAssignableFrom()
:与上述相同。对通用参数有一些支持(例如:提供通用参数T
并且它对类型int
的约束将被正确评估)但不包含带参数的泛型类型(例如:List<int>
vs { {1}}或vs List<>
(此类型来自通用方法,可能List<T>
包含一些约束))
使用所有泛型方法的输入参数调用T
,并捕获参数异常,直到没有:丑陋。
它可能还需要做一些额外的工作,因为我提供给MakeGenericMethod()
的类型可能与我收到的初始类型不完全相同(例如:我有MakeGenericMethod()
而我应该提供{{1} {}} {}
我知道这是一个复杂的主题,并不是一个小问题,但有一些问题已修复,应该让事情变得更容易:
我想知道是否有办法使用C#Framework中的现有类/方法来解决这些重载(无需重新发明轮子),如果不可能,那么可能的解决方案是什么(自定义代码) )
答案 0 :(得分:0)
考虑到您描述的先决条件,您可以这样做:
static void ExecuteCase(object a, object b)
{
var method = GetMethod(typeof(Bar).GetMethods(BindingFlags.Public | BindingFlags.Static), a.GetType(), b.GetType());
if (method != null) { method.Invoke(null, new object[] {a, b}); }
else { /* Handle method not found */ }
}
GetMethod
的位置:
private static MethodInfo GetMethod(IEnumerable<MethodInfo> methods, Type typeA, Type typeB)
{
foreach (var method in methods)
{
List<MethodInfo> candidates = new List<MethodInfo>();
if (!method.IsGenericMethod) { candidates.Add(method); }
else
{
MethodInfo genericMethod;
if (IsParameterOfGeneric(typeA, typeB) && TryMakeGenericMethod(method, typeB, typeB, out genericMethod)) candidates.Add(genericMethod);
if (IsParameterOfGeneric(typeB, typeA) && TryMakeGenericMethod(method, typeA, typeA, out genericMethod)) candidates.Add(genericMethod);
if (typeA.IsGenericType && typeB.IsGenericType && TryMakeGenericMethod(method, typeA.GetGenericArguments()[0], typeB.GetGenericArguments()[0], out genericMethod)) candidates.Add(genericMethod);
if (TryMakeGenericMethod(method, typeA, typeB, out genericMethod)) candidates.Add(genericMethod);
}
foreach (var candidate in candidates)
{
var args = candidate.GetParameters();
if (args[0].ParameterType.IsAssignableFrom(typeA) && args[1].ParameterType.IsAssignableFrom(typeB)) return candidate;
}
}
return null;
}
private static bool IsParameterOfGeneric(Type generic, Type parameter)
{
if (!generic.IsGenericType) return false;
return generic.GetGenericArguments()[0] == parameter;
}
private static bool TryMakeGenericMethod(MethodInfo method, Type typeA, Type typeB, out MethodInfo genericMethod)
{
genericMethod = null;
try
{
genericMethod = method.GetGenericArguments().Length == 1
? method.MakeGenericMethod(typeA)
: method.MakeGenericMethod(typeA, typeB);
return true;
}
catch (ArgumentException ex)
{
if (ex.Message.Contains("violates the constraint of type")) return false;
throw;
}
}