此代码未编译:
static class Foo
{
public static Bar Do(Func<Bar> f) => null;
public static Bar<TOut> Do<TOut>(Func<Bar<TOut>> f) => null;
}
public class Bar
{
}
public class Bar<TOut>
{
public static implicit operator Bar<TOut>(TOut i) => null;
}
// Here compiler complains:
// CS0029 Cannot implicitly convert type 'int' to 'Bar'
// CS1662 Cannot convert lambda expression to intended delegate type
// because some of the return types in the block
// are not implicitly convertible to the delegate return type
Foo.Do(() => 1);
我的期望是编译器看到lambda的返回类型,并且除非将int
转换为Bar<int>
,否则不能选择有效的重载。然而,我看到编译器解析到第一个方法。
spec的哪一部分定义了这个行为?
这是在方法调用中指定的,当规范讨论什么方法声明算作重载解析的候选时,对于形式M(A)
:
构造方法调用的候选方法集。对于与方法组
M
相关联的每个方法F
:
- 如果
F
是非泛型的,F
是候选的,当:
M
没有类型参数列表,F
适用于A
。- 如果
F
是泛型的,M
没有类型参数列表,F
在以下情况下是候选的:
- 类型推断成功,推断调用的类型参数列表,
- […]
仅从这些规则中,我们可以看到非泛型Do
是候选,而泛型Do
不是,因为类型推断失败。试着把非泛型的Do
注释掉,你会看到它说"不能推断类型参数"之类的话。
我没有完整的答案,但我有一些观察:
观察1:删除Do
的非通用版本:
static class Foo
{
public static Bar Do(Func<Bar> f) => null;
}
public class Bar
{
public static implicit operator Bar(int i) => null;
}
// CS0411: The type arguments for method 'Foo.Do<TOut>(Func<Bar<TOut>>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
Foo.Do(() => 1);
,编译器仍然无法解析Func<Bar<TOut>> f
。因此,问题似乎不是关于选择错误的过载,而是编译器根本无法隐式匹配() => 1
和Func<Bar<TOut>> f
。
观察2:下面的代码工作
static class Foo
{
public static Bar Do(Func<Bar> f) => null;
}
public class Bar
{
public static implicit operator Bar(int i) => null;
}
Foo.Do(() => 1);
表示编译器正在检查隐式转换。
观察3:使隐式强制转换操作符以int
作为输入,即使使操作符实际上无法使用,因为TOut
不会被解析,使编译器找到操作符:
static class Foo
{
public static Bar<TOut> Do<TOut>(Func<Bar<TOut>> f) => null;
}
public class Bar<TOut>
{
public static implicit operator Bar<TOut>(int i) => null; // Input is `int` now
}
// CS0411: The type arguments for method 'Foo.Do<TOut>(Func<Bar<TOut>>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
Foo.Do(() => 1);
所以所有这些结合起来让我认为编译器只是没有尝试哪些泛型类型会导致隐式转换工作。