为什么编译器选择通用方法而不是特定方法?

Nic*_*ada 3 .net c# generics

我有这个服务接口。

public interface IService
{
    Task SetAsync(string key, string value, TimeSpan? expiration = null);
    Task SetAsync<T>(string applicationName, string key, T value, TimeSpan? expiration = null);
}
Run Code Online (Sandbox Code Playgroud)

我想通过这样做来调用第一种方法。

service.SetAsync("Key", "Value", TimeSpan.FromMinutes(1));
Run Code Online (Sandbox Code Playgroud)

方法调用与第一个方法的契约 100% 匹配。然而编译器通过假设TimeSpan是我的泛型类型来选择第二种方法。

为什么会这样?

Jon*_*eet 8

这遵循 ECMA C# 5 标准第 12.6.4.3 节中“更好的函数成员”的规则:

为了确定更好的函数成员,按照参数表达式在原始参数列表中出现的顺序,构造了一个精简的参数列表 A,它只包含参数表达式本身。

每个候选函数成员的参数列表按以下方式构建:

  • 如果函数成员仅适用于扩展形式,则使用扩展形式。
  • 从参数列表中删除没有相应参数的可选参数
  • 参数被重新排序,以便它们与参数列表中的相应参数出现在相同的位置。

所以在这一点上,我们正在比较这两种方法,有效地:

Task SetAsync(string key, string value, TimeSpan? expiration);
Task SetAsync<T>(string applicationName, string key, T value);
Run Code Online (Sandbox Code Playgroud)

(在泛型方法版本中,expiration参数没有对应的参数,因此从参数列表中删除。)

参数列表是"Key", "Value", TimeSpan.FromMinutes(1),并T推断为TimeSpan

下一个:

给定一个参数列表 A 和一组参数表达式 { E1, E2, ..., EN } 和两个适用的函数成员 MP 和 MQ,参数类型为 { P1, P2, ..., PN } 和 { Q1, Q2, ..., QN }, MP 被定义为比 MQ 更好的函数成员,如果

  • 对于每个参数,从 EX 到 QX 的隐式转换并不比从 EX 到 PX 的隐式转换好,并且
  • 对于至少一个参数,从 EX 到 PX 的转换比从 EX 到 QX 的转换要好。

对于前两个参数,没有区别 - 因为它们string无处不在。

对于第三个参数,非泛型方法的转换是 from TimeSpanto TimeSpan?,泛型方法的转换是from TimeSpanto TimeSpan(因为TTimeSpan)。

的身份转换TimeSpanTimeSpan“更好”(如每12.6.4.4的规则)比的隐式转换TimeSpanTimeSpan?,所以对于该参数,一般的方法是“更好” ......而且也没有参数,其非泛型方法“更好”,因此总体而言泛型方法更好。

如果在此阶段没有发现任何一种方法“更好”(例如,由于参数类型为TimeSpan而不是TimeSpan?可选参数),那么非泛型方法将被决胜局规则确定为“更好”在这个阶段之后。但到那时,泛型方法已经被选中。