为什么C#编译器认为这种泛型类型推断不明确?

时间:2018-05-18 06:23:23

标签: c# generics type-inference generic-type-argument

鉴于以下课程:

    File filepath = new File("C:/UIDriverProject/UIDriverPro/Test Folder/TESTDATA/TEST.dat");
    DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
    DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
    Document doc = docBuilder.parse(filepath);

应用哪些规则来选择其两种通用方法之一?例如,在以下代码中:

$student = Student::where('roll_no','=',$roll)->first();
$book = Book::where('book_id','=',$bookno)->first();

它最终会调用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); } } (即重载1),即使它似乎与调用List<MyEnum> list; EnumHelper.GetStrings(list); 同样有效(即重载2)。

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

1 个答案:

答案 0 :(得分:7)

  

应用哪些规则来选择其两种通用方法之一?

规范中的规则 - 遗憾的是非常复杂。在ECMA C# 5 standard中,相关位从第12.6.4.3节开始(&#34;更好的函数成员&#34;)。

然而,在这种情况下,相对简单。这两种方法都是适用的,每种方法都会单独进行类型推断:

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

接下来,编译器开始检查从参数到参数的转换,以查看一次转换是否更好&#34;比另一个。这将进入第12.6.4.4节(&#34;更好地转换自表达式&#34;)。

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

  • 超载1:List<MyEnum>List<MyEnum>(推荐TEnumList<MyEnum>
  • 超载2:List<MyEnum>IEnumerable<MyEnum>TEnum推断为MyEnum

幸运的是,第一条规则可以帮助我们:

  

给定从表达式E转换为类型T1的隐式转换C1,以及从表达式E转换为类型T2的隐式转换C2,如果至少下列其中一个成立,则C1是比C2更好的转换:

     
      
  • E具有类型S,并且存在从S到T1而不是从S到T2的身份转换
  •   

List<MyEnum>List<MyEnum>的身份转换,但并非来自{{1}的身份转换}到List<MyEnum>,因此第一次转换更好。

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

你的论点是关于&#34;更一般的&#34; vs&#34;更具体&#34;如果这个早期阶段在抢七局中结束,参数将是有效的,但它并没有:&#34;更好的转换&#34;参数的参数在&#34;更具体的参数&#34;。

之前考虑

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