11

考虑

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没有 - 后者不是前者的语法糖吗?

4

2 回答 2

18

马克的回答是正确的,但可以使用更多的解释。

问题确实是由于方法组的处理方式和 lambda 的处理方式之间存在细微差别。

具体来说,细微的区别在于,仅根据参数是否匹配,而不是根据返回类型是否匹配,才认为方法组可转换为委托类型。Lambda 检查参数和返回类型。

这个奇怪规则的原因是方法组转换为委托本质上是重载解决问题的解决方案。假设 D 是委托类型double D(string s),M 是一个方法组,其中包含一个接受字符串并返回字符串的方法。在解析从 M 到 D 的转换的含义时,我们会进行重载解析,就好像您说过 M(string)。重载解析会选择接受字符串并返回字符串的 M,因此 M 可以转换为该委托类型,即使转换后会导致错误。就像如果您说“字符串 s = M(null);”,“常规”重载解决方案会成功 -- 重载解析成功,即使这会导致稍后的转换失败。

这个规则很微妙而且有点奇怪。这里的结果是您的方法组可以转换为所有不同的委托类型,这些委托类型是每个接受委托的 Sum 版本的第二个参数。由于找不到最佳转换,因此方法组上的重载Sum决策不明确。

方法组转换规则是合理的,但在 C# 中有点奇怪。我有点恼火的是它们与更“直观正确”的 lambda 转换不一致。

于 2011-10-13T02:35:03.590 回答
8

s => GetValue(s)是一个 lambda 表达式,GetValue是一个方法组,这是完全不同的东西。它们都可以被视为语法糖,new Func<string,double>(...)但它们相互关联的唯一方式是 lambda 表达式包含对GetValue(). 在转换为委托时,方法组在返回类型方面与 lambda 表达式具有不同的转换规则。请参阅为什么 Func<T> 与 Func<IEnumerable<T>> 有歧义?重载的方法组参数混淆重载决议?.

于 2011-10-12T20:14:17.197 回答