假设我有一个课程如下:
public class AcceptMethods
{
public int Accept(string s, int k = 1)
{
return 1;
}
public int Accept(object s)
{
return 2;
}
public int Accept(IEnumerable<object> s)
{
return 7;
}
public int Accept(IList<object> s)
{
return 4;
}
}
现在,如果我尝试在代码中使用它,我会使用以下内容:
object[] list = new object[] { "a", new object[0], "c", "d" };
Assert.AreEqual(7, list.Select((a)=>((int)new AcceptMethods().Accept((dynamic)a))).Sum());
它是7的原因是因为重载决议比[IList<object>
]和[IEnumerable<object>
]更喜欢[object
],因为[string
,{{1 }}优先于[int=default
]。
在我的场景中,我想使用反射获得最佳匹配重载。换句话说:'best'被定义为'c#overload resolution'。 E.g:
object
虽然我绘制的场景只有1个参数,但我寻求的解决方案可以有多个参数。
更新1 :
因为我收到一条评论,由于重载决策实施的困难(我很清楚),这对于SO来说太难了,所以我倾向于发送更新。为了给我的论证一些力量,这是我的第一次尝试,它使用处理重载决策的默认.NET绑定器:
int sum = 0;
foreach (var item in list)
{
var method = GetBestMatching(typeof(AcceptMethods).GetMethods(), item.GetType());
sum += (int)method.Invoke(myObject, new object[]{item});
}
Assert.AreEqual(7, sum);
此版本似乎已正确执行简单的重载解析,但无法使用可选参数。因为.NET afaik与我在这里展示的类型绑定一起工作,我想这个解决方案可以很容易实现。
答案 0 :(得分:4)
这是一个大量的主题,需要相当多的工作,在我看来当然不能包含在SO答案中。我建议你阅读C#规范并阅读定义重载决策的正式规则(同样,请注意通用方法)并尝试实现它们以满足您的需求。
更新
可选(即具有默认值的参数)不是一个简单的例子 - 并且Reflection绑定器根本不会尝试填充它们 - 因为它是编译器的工作来识别默认值,将它们拉出并将它们注入到这种方法的调用中。
你需要一种类似这样的多遍方法(注意 - 不包含泛型):
手动搜索一个方法,该方法的参数数量和参数类型完全匹配 您获得的参数的数量和类型。如果你找到一个匹配 - 使用它和下铺。
现在确定候选人名单&#39;过载选择的方法(通常按名称排序) - 你也可以在这里排除泛型 - 除非你也尝试绑定它们。)
如果这些方法都没有可选参数,那么您可以继续按照您的问题使用默认绑定器来查找匹配项(如果没有,则需要基于参数/参数排序算法类型 - 如果是,请跳至5)。
重新运行3)中构建的候选列表,拉出所有可选参数并将其默认值合并到您自己的参数列表中(您可能需要为此方法构建一组单独的参数)点,包括你提供的那些,还有默认值。
为3)中可能构建的这些方法运行您的排名算法,可能还有4)确定最佳匹配(您似乎对此有了很好的处理,所以我不打算在这里完成所有操作 - 这不是一个简单的算法,我坦白地说,不能在这里逐字引用!)。
您的排名算法应该产生明确的获胜方法 - 即具有独特的高分或类似。如果你得到一个明确的赢家,那就是你绑定的那个。否则你就会有一种模棱两可的态度,而且你必须把它搞得一团糟。
此时您可能对我自己的SO感兴趣 - Default parameters and reflection: if ParameterInfo.IsOptional then is DefaultValue always reliable? - 这可以帮助您识别具有默认参数的方法,以及如何解除它们。
答案 1 :(得分:2)
对于其他想要进行运行时重载解析的人来说,这是一个关于如何实现它的相当完整的描述。
重要的一点是,“动态”技巧在所有场景中都不起作用(特别是:泛型);似乎编译器比运行时行为更灵活。
另请注意,这不是一个完整的算法/实现(或者至少我认为它不是),但在大多数情况下仍然有效。我发现这在我到目前为止遇到的所有情况下都有效,包括阵列协方差等困难情况。
评分算法的工作原理如下:
兼容性分数是类型类型A和类型B之间最严格的转换(包括和协方差,逆变)。例如,string []有1个转换为IList(使用object []然后IList)和2个转换为IEnumerable(1.通过使用object []然后IEnumerable或2.通过IEnumerable)。因此,IList是更严格的转换,因此被选中。
计算转化次数很简单:
int cnt = CountCompatible(parameter.ParameterType, sourceType.GetInterfaces()) +
CountCompatible(parameter.ParameterType, sourceType.GetBaseTypes()) +
CountCompatible(parameter.ParameterType, new Type[] { sourceType });
[...]
private static int CountCompatible(Type dst, IEnumerable<Type> types)
{
int cnt = 0;
foreach (var t in types)
{
if (dst.IsAssignableFrom(t))
{
++cnt;
}
}
return cnt;
}
为了确保在使用更严格的转换时选择更好的分数,我将分数计算为'得分= 5 - 1.0 /(cnt + 2);'。 +2确保您永远不会除以0或1,从而得分在4到5之间。
要执行重载解析,请选择所有参数的最低得分方法。确保在调用时正确输入默认方法参数(请参阅上面Andras的优秀链接),并确保在返回方法之前填写泛型参数。如果您遇到最佳方法的绘制:最佳解决方案是抛出异常。
如果你想知道,是的......要让它全部正常工作是相当多的工作......我计划在完成之后在我的框架中提供一个可用的版本。 (你会看到我的个人资料有一个工作网站链接的那一刻:-))