我今天对方法解决方案感到惊讶。
这是一个典范的代码:
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);
}
}
正如您所看到的,方法映射有两个过载,一个当属性的类型相同时,一个源属性是一个枚举,正确的属性是一个数组。
然后,当我调用两侧数组的方法时,它调用了第二个过载,但是由于类型与我期望的第一个过载完全相同。
我认为第一个过载的评分会更好,因为它取决于比第二个较少的通用参数,并且与我传递给该方法的参数类型更适合。
有人可以解释为什么编译器选择调用第二个过载,而不是第一个过载?
谢谢。
超载分辨率很复杂,您可以阅读规格以了解原因。我很确定的一件事是,考虑到哪个过载更好时,它并不需要指定更少的通用参数(尽管它将需要非一般的通用,但是当两者都是通用时,它会将它们视为平等)。
查看过载时,除了第二个参数是TMember
还是TMember[]
。
规格关于选择最具体的成员的经常讨论,我无法确定在这里实际上适用的哪一部分(在许多地方谈论在x更具体时更喜欢X而不是y)。我本来以为是第7.6.5.1节(C#5规格的),它是构造候选列表的地方,或者是处理过载分辨率的7.5.3节。但是,前者似乎并不排除任何一种方法过载,而后者的阅读仅处理了通用参数之后的参数。规格中可能还有其他某个地方来处理此问题(例如,何时添加类型参数)。
用羊毛术语,我相信编译器正在考虑TMember[]
比TMember
更具体。这可以广泛地认为是正确的,因为TMember
比TMember []接受更多的东西,因此TMember[]
更具体。
第一种方法TMember
和第二种方法的匹配TSourceCollection
对于满足where TSourceCollection : IEnumerable<TMember>
条件的任何类型的值均等。
与TMember
相比,TMember[]
类型是byte[]
的更详细的类型匹配。因此,这应该是第二种方法得分优于第一个方法的点。因此,这种"更好"的匹配击败了事实,该方法两个具有更多的通用参数。