C# 为什么编译器选择泛型方法而不是特定方法?

C# 为什么编译器选择泛型方法而不是特定方法?,c#,.net,generics,C#,.net,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); } 方法调用与第一个方法的约定100%匹配。然而,编译器通过假设Time

我有这个服务接口

public interface IService
{
    Task SetAsync(string key, string value, TimeSpan? expiration = null);
    Task SetAsync<T>(string applicationName, string key, T value, TimeSpan? expiration = null);
}
方法调用与第一个方法的约定100%匹配。然而,编译器通过假设
TimeSpan
是我的泛型类型来选择第二种方法


为什么会发生这种情况?

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

为了确定更好的函数成员,将构造一个精简的参数列表a,该列表仅包含参数表达式本身,其顺序与它们在原始参数列表中出现的顺序相同

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

  • 如果函数成员仅适用于扩展表单,则使用扩展表单
  • 没有相应参数的可选参数将从参数列表中删除
  • 参数被重新排序,以便它们出现在参数列表中与相应参数相同的位置
因此,在这一点上,我们有效地比较了这两种方法:

Task SetAsync(字符串键、字符串值、TimeSpan?过期);
任务SetAsync(字符串applicationName、字符串键、T值);
(在泛型方法版本中,
expiration
参数没有相应的参数,因此将其从参数列表中删除。)

参数列表是
“Key”、“Value”、TimeSpan.FromMinutes(1)
T
被推断为
TimeSpan

下一步:

给定一个参数列表A,其中包含一组参数表达式{E1,E2,…,EN},以及参数类型为{P1,P2,…,PN}和{Q1,Q2,…,QN}的两个适用函数成员MP和MQ,则MP被定义为比MQ更好的函数成员

  • 对于每个参数,从EX到QX的隐式转换并不比从EX到PX的隐式转换好,并且
  • 对于至少一个参数,从EX到PX的转换比从EX到QX的转换要好
对于前两个参数,没有区别——因为它们到处都是
string

对于第三个参数,非泛型方法的转换是从
TimeSpan
TimeSpan?
,而泛型方法的转换是从
TimeSpan
TimeSpan
(因为
T
TimeSpan

TimeSpan
TimeSpan
的身份转换比
TimeSpan
TimeSpan?
的隐式转换“更好”(根据12.6.4.4的规则),因此对于该参数,通用方法是“更好”。。。而且没有非泛型方法“更好”的参数,因此总体而言泛型方法更好


如果在此阶段未发现任何一种方法“更好”(例如,由于可选参数的参数类型为
TimeSpan
,而不是
TimeSpan?
),则此阶段后的平局规则将确定非通用方法“更好”。但是到那时,通用方法已经被选中。

它已经为后面的参数设置了默认值,这会使事情变得更糟。我将尝试找到一个apt副本。运行其他方法的一个简单解决方法是显式指定其参数名:
service.SetAsync(key:“key”,“Value”,TimeSpan.frommins(1))()尝试
新服务().SetAsync(“键”,“值”,新时间跨度?())。原因
TimeSpan.FromMinutes(1)
TimeSpan
不是
TimeSpan?
@Damien\u不信者:我希望不需要默认参数来使第一种方法更可取。正在调查…如果您传递一个非空值,那么它将采用第一个方法,或者将方法上的
TimeSpan?
替换为
TimeSpan
,它也将采用第一个方法。a-ha,遵循规则,现在一切都有意义了。谢谢你把这件事弄清楚。如果我说这件事没有让我昨晚失去一个小时的睡眠,那我就是在撒谎。尤其是12.6.4.4中的规则使得这很容易混淆。我的直觉告诉我,我必须指定
,让编译器选择泛型方法。但这就是12.6.4.4的用武之地。
service.SetAsync("Key", "Value", TimeSpan.FromMinutes(1));