为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

C#
慕尼黑的夜晚无繁华 2021-06-10 14:13:11
鉴于以下类: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);    }}应用什么规则来选择其两种通用方法之一?例如,在以下代码中:List<MyEnum> list;EnumHelper.GetStrings(list);它最终会调用EnumHelper.GetStrings<List<MyEnum>>(List<MyEnum>)(即 Overload 1),即使它看起来同样有效EnumHelper.GetStrings<MyEnum>(IEnumerable<MyEnum>)(即 Overload 2)。例如,如果我完全删除重载 1,那么调用仍然可以正常编译,而不是选择标记为重载 2 的方法。这似乎使泛型类型推断有点危险,因为它正在调用一个直观上看起来更糟糕匹配的方法. 我正在传递一个 List/Enumerable 作为类型,它看起来非常具体,似乎应该匹配具有类似参数的方法(IEnumerable<TEnum>),但它选择了具有更通用的通用参数的方法(TEnum value)。
查看完整描述

1 回答

?
温温酱

TA贡献1752条经验 获得超4个赞

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

规范中的规则 - 不幸的是,这些规则非常复杂。在ECMA C# 5 标准中,相关位从第 12.6.4.3 节(“更好的函数成员”)开始。

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

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

  • 对于方法2,TEnum推断为MyEnum

接下来,编译器开始检查从参数到参数的转换,以查看一种转换是否比另一种“更好”。这进入第 12.6.4.4 节(“更好地从表达式转换”)。

在这一点上,我们正在考虑这些转换:

  • 重载 1: List<MyEnum>to List<MyEnum>(TEnum推断为List<MyEnum>)

  • 重载 2: List<MyEnum>to IEnumerable<MyEnum>(TEnum推断为MyEnum)

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

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

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

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

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

如果此早期阶段以抢七局结束,您关于“更一般”与“更具体”参数的论点将是有效的,但事实并非如此:在“更具体的参数”之前考虑参数到参数的“更好的转换” .

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


查看完整回答
反对 回复 2021-06-20
  • 1 回答
  • 0 关注
  • 154 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信