2

今天我对方法解析的工作方式感到惊讶。

下面以代码为例:

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 有两种重载,一种是属性类型相同时,另一种是源属性是可枚举且正确的属性是数组时。

然后,当我在两侧调用带有数组的方法时,它调用第二个重载,但由于类型完全相同,我希望调用第一个重载。

我认为第一个重载会有更好的得分,因为它依赖的泛型参数比第二个少,而且它更适合我传递给方法的参数类型。

有人可以解释为什么编译器选择调用第二个重载而不是第一个重载吗?

谢谢。

4

2 回答 2

1

第一种方法TMember和第二种方法的匹配TSourceCollection对于任何满足where TSourceCollection : IEnumerable<TMember>条件的类型都是相等的。

与相比,类型TMember[]是更详细的类型匹配。所以这应该是第二种方法比第一种方法得分更高的点。因此,这种“更好”的匹配排除了方法二具有更多通用参数的事实。byte[]TMember

于 2016-06-16T12:10:03.790 回答
1

重载解决方案很复杂,您可以阅读规范以了解原因。我很确定的一件事是,在考虑哪个重载更好时,它不考虑指定较少泛型参数的需要(尽管它会采用非泛型而不是泛型,但当两者都是泛型时,它认为它们是相等的)。

在查看重载时,它可以从中进行选择,除了第二个参数是TMember或之外,它们都相等TMember[]

规范谈了很多关于选择最具体的成员的内容,我无法确定哪个部分在这里实际适用(有很多地方谈到当 X 更具体时更喜欢 X 而不是 Y)。我会认为它是第 7.6.5.1 节(c#5 规范)是它构造候选列表的地方,或者是第 7.5.3 节它处理重载决议的地方。然而,前者似乎不排除任何一种方法重载,而后者在我的阅读中只处理在通用参数被替换后的参数,此时它们是相同的。规范中可能有其他地方处理这个问题(例如,当它推断类型参数时)。

虽然我相信编译器正在考虑TMember[]TMember. 这可以被广泛认为是正确的,因为它TMember会接受比 TMember[] 更多的东西,所以TMember[]更具体。

于 2016-06-16T12:45:17.160 回答