具有泛型和类型推理的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[],所以我希望第一个被选中……感谢您的精彩解释,这现在是有意义的。