使用泛型和类型推断的C#方法解析

时间:2016-06-16 09:47:28

标签: c# generics type-inference method-resolution-order

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

以下是代码示例:

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有两个重载,一个是属性类型相同的,一个是source属性是可枚举的,右属性是数组。

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

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

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

感谢。

2 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:1)

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

当查看它可以选择的重载时,它们都等于第二个参数是TMember还是TMember[]

规范谈到了很多关于选择最具体的成员的问题,我无法确定哪个部分在这里实际适用(当X更具体时,有许多地方讨论优先选择X而不是Y)。我原本认为它是第7.6.5.1节(c#5规范),它是构建候选列表的部分,或7.5.3部分,它处理重载决策。然而,前者似乎并没有排除任何一种方法过载,而后者在我的阅读中只处理了一般参数之后的参数,在这一点上它们是相同的。可能是规范中的其他地方处理了这个问题(例如,当它推断出类型参数时)。

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