C# 如何对运行时已知的类型执行方法重载解析?
我有一个包含方法重载的类:C# 如何对运行时已知的类型执行方法重载解析?,c#,generics,reflection,runtime,overloading,C#,Generics,Reflection,Runtime,Overloading,我有一个包含方法重载的类: 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&l
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)
对于两种给定的参数类型(例如:typeof(int)
,typeof(List)
),我希望动态选择最合适的方法。
到目前为止我尝试/检查的内容:
:该方法不支持泛型Binder.SelectMethod()
:同上。对泛型参数有一些支持(例如:提供泛型参数typeof().IsAssignableFrom()
,它对类型T
的约束将得到正确的评估),但不支持带有参数的泛型类型(例如:int
vsList
或vsList
(此类型来自泛型方法,可能List
包含一些约束)T
- 使用所有泛型方法的输入参数调用
,并捕获参数异常,直到没有异常为止:丑陋。 它可能需要做一些额外的工作,因为我将提供给MakeGenericMethod()
的类型可能与我收到的初始类型不完全相同(例如:我有MakeGenericMethod()
,而我应该提供typeof(List),typeof(int)
给typeof(int)
)MakeGenericMethod()
- 所有方法都有两个参数和相同的返回类型
- 没有可选类型,没有数组
- 泛型方法不再有一个或两个泛型参数,参数的顺序始终相同
- 不需要支持协方差/逆变
- 方法已按“优先级”排序。这就是为什么没有约束的泛型方法位于此列表的末尾。 一旦一种方法匹配,我们就可以接受它,而忽略其他方法
我想知道是否有办法使用C#Framework中的现有类/方法解决这些重载问题(无需重新发明轮子),如果不可能,可能的解决方案是什么(自定义代码)考虑到您描述的先决条件,您可以这样做:
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;
}
}
private static MethodInfo GetMethod(IEnumerable方法,类型A,类型B)
{
foreach(方法中的var方法)
{
列表候选者=新列表();
如果(!method.IsGenericMethod){candidates.Add(method);}
其他的
{
方法信息一般方法;
如果(IsParameterOfGeneric(typeA,typeB)和&TryMakeGenericMethod(method,typeB,typeB,out genericMethod))候选。添加(genericMethod);
如果(IsParameterOfGeneric(typeB,typeA)和&TryMakeGenericMethod(method,typeA,typeA,out genericMethod))候选项。添加(genericMethod);
如果(typeA.IsGenericType&&typeB.IsGenericType&&TryMakeGenericMethod(方法,typeA.GetGenericArguments()[0],typeB.GetGenericArguments()[0],out genericMethod))候选。添加(genericMethod);
if(trymakenergicmethod(method,typeA,typeB,out-genericMethod))候选者。Add(genericMethod);
}
foreach(候选人中的var候选人)
{
var args=candidate.GetParameters();
if(args[0].ParameterType.IsAssignableFrom(typeA)和&args[1].ParameterType.IsAssignableFrom(typeB))返回候选者;
}
}
返回null;
}
私有静态bool IsParameterOfGeneric(类型泛型,类型参数)
{
如果(!generic.IsGenericType)返回false;
返回generic.GetGenericArguments()[0]==参数;
}
私有静态bool-trymakenericmethod(MethodInfo方法,typeA类型,typeB类型,out-MethodInfo-genericMethod)
{
genericMethod=null;
尝试
{
genericMethod=method.GetGenericArguments().Length==1
?方法。MakeGenericMethod(类型A)
:方法.MakeGenericMethod(typeA,typeB);
返回true;
}
捕获(参数异常)
{
if(例如Message.Contains(“违反类型的约束”))返回false;
投掷;
}
}
仅供参考,谢谢你的回答。无论如何,签名
ExecuteCase
方法在我看来并不好,因为它使用泛型参数,这意味着我知道输入类型是运行时(我不知道操作的全部要点)。另外:如果输入类型是List,int
,匹配的方法是Foo(List a,tb)
,那么MakeGenericMethod try-and-catch技术是否有效?(那么泛型参数应该是int
而不是List
和int
)。请参阅编辑。修复了上面评论中提到的案例问题。还将ExecuteCase
从泛型更改为object
。我使用了一种自定义解决方案,其实现与microsoft的IsAssignableFrom()
方法非常类似:区别在于没有直接检查类型是否相等(例如:this==c
)它使用了一个自定义方法来处理泛型(例如:typeof(List==typeof(List)
将是真的),它工作得非常好。如果我有时间,我会发布解决方案(在清理之后)。
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;
}
}