为什么 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# 添加新功能时,它都会影响重载解析:(
给出以下 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# 添加新功能时,它都会影响重载解析:(