今天我对方法解析的工作方式感到惊讶。
以下是示例代码:
class Program
{
static class Mapper<TSource, TTarget>
{
public static void Map<TMember>(Expression<Func<TSource, TMember>> source, Expression<Func<TTarget, TMember>> target)
{
Console.WriteLine("A");
}
public static void Map<TMember, TSourceCollection>(Expression<Func<TSource, TSourceCollection>> source, Expression<Func<TTarget, TMember[]>> target)
where TSourceCollection : IEnumerable<TMember>
{
Console.WriteLine("B");
}
}
class A
{
public byte[] prop { get; set; }
}
class B
{
public byte[] prop { get; set; }
}
static void Main(string[] args)
{
Mapper<A, B>.Map(x => x.prop, x => x.prop);
}
}
如您所见,Map 方法有两个重载,一种是当属性的类型相同时,另一种是当源属性是可枚举且右侧属性是数组时。
然后,当我调用两侧都有数组的方法时,它会调用第二个重载,但由于类型完全相同,我预计会调用第一个重载。
我认为第一个重载会有更好的得分,因为它比第二个重载依赖更少的通用参数,并且它更适合我传递给该方法的参数类型。
有人可以解释一下为什么编译器选择调用第二个重载而不是第一个重载吗?
谢谢。
最佳答案
重载解析很复杂,您可以阅读规范来了解原因。我非常确定的一件事是,在考虑哪种重载更好时,它不会考虑指定较少通用参数的需要(尽管它将采用非通用而不是通用,但当两者都是通用时,它认为它们相等)。
在查看重载时,它可以从中进行选择,除了第二个参数是 TMember
还是 TMember[]
之外,它们都是相等的。
规范谈论了很多关于选择最具体的成员的问题,但我无法确定哪一部分实际上适用于这里(有很多地方都谈到当 X 更具体时更喜欢 X 而不是 Y)。我本以为它要么是(c#5 规范的)第 7.6.5.1 节(它构造候选列表),要么是第 7.5.3 节(它处理重载解析)。然而,前者似乎并没有排除任何一种方法重载,而根据我的阅读,后者仅处理通用参数被替换后的参数,此时它们是相同的。规范中可能有其他地方处理这个问题(例如,当它推断类型参数时)。
笼统地说,我相信编译器认为 TMember[]
比 TMember
更具体。从广义上讲,这是正确的,因为 TMember
比 TMember[] 接受的内容更多,因此 TMember[]
更加具体。
关于使用泛型和类型推断的 C# 方法解析,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37855630/