考虑
void Main()
{
var list = new[] {"1", "2", "3"};
list.Sum(GetValue); //error CS0121
list.Sum(s => GetValue(s)); //works !
}
double GetValue(string s)
{
double val;
double.TryParse(s, out val);
return val;
}
CS0121 错误的描述是
以下方法或属性之间的调用是不明确的:
和'System.Linq.Enumerable.Sum<string>(System.Collections.Generic.IEnumerable<string>, System.Func<string,decimal>)'
)''System.Linq.Enumerable.Sum<string>(System.Collections.Generic.IEnumerable<string>, System.Func<string,decimal?>
我不明白的是,
s => GetValue(s)
为编译器提供了哪些信息,而GetValue
则没有——后者不是前者的语法糖吗?
马克的答案是正确的,但需要更多解释。
问题确实是由于方法组的处理方式和 lambda 的处理方式之间存在细微的差异。
具体而言,细微的区别在于,方法组仅根据参数是否匹配而被视为可转换为委托类型,而不是还基于返回类型是否匹配。Lambdas检查参数和返回类型。
这个奇怪规则的原因是方法组转换为委托本质上是“重载解析”问题的解决方案。假设 D 是委托类型 double D(string s)
,M 是一个方法组,其中包含一个接受字符串并返回字符串的方法。当解析从 M 到 D 的转换的含义时,我们会像您所说的 M(string) 一样进行重载解析。重载解析将选择接受字符串并返回字符串的 M,因此 M 可以转换为该委托类型
即使转换稍后会导致错误。正如如果您说“string s = M(null);”,“常规”重载解析就会成功。 -- 重载解析成功,即使这会导致稍后转换失败。 这个规则很微妙,而且有点奇怪。这里的结果是你的方法组可以转换为“所有不同的委托类型”,它们是“每个接受委托的 Sum 版本”的第二个参数。由于无法找到最佳转换,因此方法组
Sum
上的重载解析不明确。 方法组转换规则看似合理,但在 C# 中有点奇怪。我有点恼火,因为它们与更“直观正确”的 lambda 转换不一致。
s => GetValue(s)
是一个lambda表达式,
GetValue
new Func<string,double>(...)
的语法糖,但它们彼此相关的唯一方式是 lambda 表达式包含对
GetValue()
的调用。在转换为委托时,方法组在返回类型方面具有与 lambda 表达式不同的转换规则。请参阅 为什么 Func与 Func
> 不明确?和
重载方法组参数混淆重载解析?。