方法推断不适用于方法组。

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 相比,会给编译器提供什么信息 - 是否后者只是前者的语法糖?
2个回答

18

马克的答案是正确的,但需要更详细的解释。

问题确实源于方法组和 Lambda 表达式处理方式之间微妙的差异。

具体来说,微妙的差异在于:一个方法组仅考虑参数是否匹配而不考虑返回类型就被视为可转换为委托类型,而 Lambda 表达式会检查参数和返回类型都是否匹配。

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

这个规则有些微妙和奇怪。在这里的结果是,你的方法组可转换为每个版本 Sum 接受的所有不同委托类型的第二个参数。由于找不到最佳的转换,因此方法组 Sum 的重载解析是有歧义的。

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


如果你有一台时间机器,你会如何改变它们?你会让他们考虑返回类型,比如lambda吗?还是你会做出其他微妙的改变? - configurator
有趣!我在LinqPad中试玩了一下,即使我有double D1(string s)int D1(string s),一个带有string M(string)签名的方法组也会产生模棱两可的调用错误 - 即使这两个签名都不匹配。这与lambda不同,后者会尝试它们所有并在最后(或首次)尝试时给出转换错误。 - Ohad Schneider
然而,如果没有重载解析问题(并且使用了错误的签名),方法组错误(“M 具有错误的返回类型”)比 Lambda 的错误(“无法隐式将类型 'string' 转换为 'double'”)略微更具信息性。 - Ohad Schneider

8
s => GetValue(s) 是一个 lambda 表达式,GetValue 是一个方法组,它们是完全不同的东西。它们都可以被视为 new Func<string,double>(...) 的语法糖,但它们之间唯一相关的方式就是 lambda 表达式包含了对 GetValue() 的调用。在转换为委托时,方法组和 lambda 表达式在返回类型方面的转换规则是不同的。请参考 Why is Func<T> ambiguous with Func<IEnumerable<T>>?Overloaded method-group argument confuses overload resolution?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接