使用泛型和隐式转换的重载决议

Overload resolution with generics and implicit conversion

此代码未编译:

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 的 return 类型,并且除非 int 转换为 Bar<int>,否则无法选择有效的重载。 但是,我看到编译器解析为第一种方法。

规范的哪一部分定义了这种行为?

这在 Method Invocations 中指定,当规范讨论什么方法声明算作重载解析的 候选 时,对于形式的调用 M(A):

The set of candidate methods for the method invocation is constructed. For each method F associated with the method group M:

  • If F is non-generic, F is a candidate when:
    • M has no type argument list, and
    • F is applicable with respect to A.
  • If F is generic and M has no type argument list, F is a candidate when:
    • Type inference succeeds, inferring a list of type arguments for the call, and
    • [...]

仅从这些规则中,我们可以看出非泛型 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);

所以所有这些结合起来让我觉得编译器只是没有尝试哪些泛型类型会导致隐式转换起作用。