【问题标题】:C# method resolution with generic and type inference具有泛型和类型推断的 C# 方法解析
【发布时间】:2016-06-16 09:47:27
【问题描述】:

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

以代码为例:

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

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

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

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

谢谢。

【问题讨论】:

  • 您使用 A 类和 B 类调用映射器,它们不是数组。它们都有一个字节数组的属性,但它们的类型不同。
  • 将 byte[] 更改为 int 并使用第一个。所以我猜这是因为字节数组是一个 IEnumerable。
  • 谢谢,但解决方案应该发生在 TMember,而不是 TSource 或 TTarget。 TSource 和 TTarget 本质上是不同的。
  • 是的,字节数组和 IEnumerable 是兼容的。显然,两个重载都可以工作,但是当第一个说“如果两者都一样,带我走”时,为什么要选择第二个。在我的例子中,两者都是 byte[] 所以我希望第一个被选中...

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


【解决方案1】:

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

查看重载时,除了第二个参数是TMember 还是TMember[] 之外,它可以从中进行选择。

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

虽然我相信编译器认为TMember[]TMember 更具体,但用粗略的术语来说。这可以被广泛认为是正确的,因为TMember 将接受比 TMember[] 更多的东西,所以TMember[] 更具体。

【讨论】:

  • 感谢您的精彩解释,现在说得通了。
【解决方案2】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-08
    • 1970-01-01
    • 2010-12-20
    相关资源
    最近更新 更多