泛型类型的泛型方法重载不明确

kni*_*ttl 7 c# generics .net-4.0 ambiguous overload-resolution

假设我有两种通用的,重载的表单方法:

public string Do<T>(T maybeValue, Func<T, string> func)
  where T : class
{
  if(maybeValue == null) return null;
  return func(maybeValue);
}

public string Do<T>(T? maybeValue, Func<T, string> func)
  where T : struct
{
  if(!maybeValue.HasValue) return null;
  return func(maybeValue.Value);
}
Run Code Online (Sandbox Code Playgroud)

现在,使用类型为nullable的变量调用方法,C#拒绝编译并说两次重载之间的调用是不明确的:

int? maybeX = 3;
Do(maybeX, x => x.ToString());
Run Code Online (Sandbox Code Playgroud)

以下方法或属性之间的调用不明确:' Program.Do<int?>(int?, System.Func<int?,string>)'和' Program.Do<int>(int?, System.Func<int,string>)'

简单的修复是在调用方法时包含泛型参数,或指定lambda参数的类型:

Do<int>(maybeX, x => x.ToString());
Do(maybeX, (int x) => x.ToString());
Run Code Online (Sandbox Code Playgroud)

有趣的是,int?在调用期间选择泛型类型将无法编译

该类型必须是引用类型,以便在泛型类型或方法中将其用作参数"T".

怎么会?显然,只有两个重载中的一个可以与类型的值一起使用int?,但编译器说调用是不明确的.我是否可以进一步约束方法以帮助编译器决定调用哪个方法,而不必让调用代码明确指定类型?

Den*_*s_E 4

约束where T : class不是方法签名的一部分。该检查稍后在方法重载选择过程中进行。
这就是为什么它被认为是含糊不清的。在检查任何约束之前,这两种方法都是匹配的。
如果您明确地说Do<int?>只有第一个方法是匹配的,但随后约束“启动”并确定它无效,因为它int?不是引用类型。

如果将其更改为:则将选择第二种方法:

public static string Do<T>(T? maybeValue, Func<T?, string> func)
    where T : struct
Run Code Online (Sandbox Code Playgroud)