给定以下类:
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>)
(即重载 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 节(“更好的函数成员”)开始。
但是,在这种情况下,它相对简单。这两种方法都适用,每种方法分别进行类型推断:
- 对于方法 1,
TEnum
推断为List<MyEnum>
- 对于方法 2,
TEnum
推断为MyEnum
接下来,编译器开始检查参数到形参的转换,看一个转换是否比另一个“更好”。这进入第 12.6.4.4 节(“更好地从表达式转换”)。
此时我们正在考虑这些转化:
- 重载 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 被视为更好的函数成员。
如果这个早期阶段以平局结束,你关于“更通用”与“更具体”参数的争论将是有效的,但事实并非如此:参数到参数的“更好转换”在“更多”之前被考虑具体参数”。
一般来说,重载解析都非常复杂。它必须考虑继承、泛型、无类型参数(例如 null 文字、默认文字、匿名函数)、参数数组和所有可能的转换。几乎每次向 C# 添加新功能时,它都会影响重载解析:(
关于c# - 为什么 C# 编译器不认为这种泛型类型推断有歧义?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50404989/