为什么 C# 编译器不认为这种泛型类型推断有歧义?

Why doesn't the C# compiler consider this generic type inference ambiguous?

给出以下 class:

public static class EnumHelper
{
    //Overload 1
    public static List<string> GetStrings<TEnum>(TEnum value)
    {
        return EnumHelper<TEnum>.GetStrings(value);
    }

    //Overload 2
    public static List<string> GetStrings<TEnum>(IEnumerable<TEnum> value)
    {
        return EnumHelper<TEnum>.GetStrings(value);
    }
}

select 它的两个泛型方法之一适用什么规则?例如,在下面的代码中:

List<MyEnum> list;
EnumHelper.GetStrings(list);

它最终调用 EnumHelper.GetStrings<List<MyEnum>>(List<MyEnum>)(即重载 1),即使调用 EnumHelper.GetStrings<MyEnum>(IEnumerable<MyEnum>)(即重载 2)似乎同样有效。

例如,如果我完全删除重载 1,那么调用仍然可以正常编译,而不是选择标记为重载 2 的方法。这似乎使泛型类型推断有点危险,因为它调用了一个直观的方法似乎是一场更糟糕的比赛。我正在传递一个 List/Enumerable 作为类型,这看起来非常具体并且似乎应该匹配具有类似参数 (IEnumerable<TEnum>) 的方法,但它选择具有更通用的通用参数 (TEnum value).

What rules are applied to select one of its two generic methods?

规范中的规则 - 不幸的是,它们极其复杂。在 ECMA C# 5 standard 中,相关位从第 12.6.4.3 节开始 ("better function member")。

但是,在这种情况下,相对简单。两种方法都适用,每种方法分别进行类型推断:

  • 对于方法 1,TEnum 被推断为 List<MyEnum>
  • 对于方法2,TEnum被推断为MyEnum

接下来,编译器开始检查从参数到参数的转换,以查看一个转换是否比另一个转换 "better"。这进入第 12.6.4.4 节 ("better conversion from expression")。

此时我们正在考虑这些转换:

  • 重载 1:List<MyEnum>List<MyEnum>(因为 TEnum 被推断为 List<MyEnum>
  • 重载 2:List<MyEnum>IEnumerable<MyEnum>(因为 TEnum 被推断为 MyEnum

幸运的是,第一条规则对我们有帮助:

Given an implicit conversion C1 that converts from an expression E to a type T1, and an implicit conversion C2 that converts from an expression E to a type T2, C1 is a better conversion than C2 if at least one of the following holds:

  • E has a type S and an identity conversion exists from S to T1 but not from S to T2

List<MyEnum>List<MyEnum>的身份转换,但不是身份从 List<MyEnum> 转换为 IEnumerable<MyEnum>,因此第一次转换更好。

没有任何其他转换需要考虑,因此重载 1 被视为更好的函数成员。

你关于 "more general" 与 "more specific" 参数的争论如果这个早期阶段以平局结束则有效,但它不是:"better conversion" 对于参数的参数在 "more specific parameters".

之前考虑

一般来说,两种重载解析都非常复杂。它必须考虑继承、泛型、无类型参数(例如 null 文字、默认文字、匿名函数)、参数数组和所有可能的转换。几乎每次向 C# 添加新功能时,它都会影响重载解析:(