具有泛型和类型推理的C#方法解析

具有泛型和类型推理的C#方法解析,c#,generics,type-inference,method-resolution-order,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>> targe

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

以下是代码示例:

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);
    }
}
类程序
{
静态类映射器
{
公共静态无效映射(表达式源、表达式目标)
{
控制台。写入线(“A”);
}
公共静态无效映射(表达式源、表达式目标)
其中TSourceCollection:IEnumerable
{
控制台。写入线(“B”);
}
}
甲级
{
公共字节[]prop{get;set;}
}
B类
{
公共字节[]prop{get;set;}
}
静态void Main(字符串[]参数)
{
Map(x=>x.prop,x=>x.prop);
}
}
正如您所看到的,方法映射有两个重载,一个是当属性的类型相同时,另一个是当源属性是可枚举的且正确的属性是数组时

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

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

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


谢谢。

对于满足
其中TSourceCollection:IEnumerable
条件的任何类型,第一种方法
TMember
和第二种方法
TSourceCollection
的匹配值相等


TMember
相比,类型
TMember[]
byte[]
更详细的类型匹配。因此,这应该是第二种方法比第一种方法得分更好的地方。因此,这种“更好”的匹配超过了这样一个事实,即方法2具有更多的通用参数。

重载解析非常复杂,您可以阅读规范以了解原因。有一点我非常肯定,那就是在考虑哪种重载更好时,它不需要指定更少的泛型参数(尽管它将非泛型泛型,但是当两者都是泛型时,它认为它们是相等的)。p> 查看重载时,它可以从以下选项中进行选择:除了第二个参数是
TMember
还是
TMember[]

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


虽然我认为编译器认为
TMember[]
TMember
更为具体,但用含糊不清的话来说,这是一个错误。这可以被广泛地看作是正确的,因为
TMember
将接受比TMember[]更多的东西,因此
TMember[]
更具体。

您使用类a和B调用映射器,它们不是数组。它们都有一个字节数组属性,但它们的类型不同。请将byte[]更改为int,然后使用第一个。所以我猜这是因为字节数组是IEnumerable。谢谢,但解析应该发生在TMember上,而不是TSource或TTarget上。TSource和TTarget本质不同。是的,字节数组和IEnumerable是兼容的。显然,这两种重载都可以工作,但为什么选择第二种重载,而第一种重载说“如果两种重载都相同,请接受我”。在我的示例中,这两个都是byte[],所以我希望第一个被选中……感谢您的精彩解释,这现在是有意义的。