方法推理不适用于方法组
考虑
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<字符串>(System.Collections.Generic.IEnumerable<字符串>, System.Func<字符串,十进制>)'
和'System.Linq.Enumerable.Sum<字符串>(System.Collections.Generic.IEnumerable<字符串>, System.Func<字符串,十进制?>
)'
我不明白的是,s => 是什么信息? GetValue(s)
为编译器提供了 GetValue
所没有的功能 - 后者不是前者的语法糖吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
马克的答案是正确的,但可以使用更多解释。
这个问题确实是由于方法组的处理方式和 lambda 的处理方式之间存在细微的差异造成的。
具体来说,细微的区别在于,方法组被认为可转换为委托类型仅基于参数是否匹配,而不是基于参数是否匹配。返回类型匹配。Lambdas 检查参数和返回类型。
这个奇怪规则的原因是方法组到委托的转换本质上是重载解析问题的解决方案。假设 D 是委托类型 double D(string s),M 是一个方法组,其中包含一个接受字符串并返回字符串的方法。当解析从 M 到 D 的转换的含义时,我们会像您所说的 M(string) 一样进行重载解析。重载解析将选择接受字符串并返回字符串的 M,因此 M 可以转换为该委托类型,即使转换稍后会导致错误。正如如果您说“string s = M(null);”,“常规”重载解析就会成功。 -- 重载解析成功,即使这会导致稍后转换失败。
这个规则很微妙,而且有点奇怪。这里的结果是,您的方法组可以转换为所有不同的委托类型,它们是每个接受委托的 Sum 版本的第二个参数。由于无法找到最佳转换,因此方法组
Sum
的重载解析不明确。方法组转换规则看似合理,但在 C# 中有点奇怪。我有点恼火,因为它们与更“直观正确”的 lambda 转换不一致。
Mark's answer is correct but could use a bit more explanation.
The problem is indeed due to a subtle difference between how method groups are handled and how lambdas are handled.
Specifically, the subtle difference is that a method group is considered to be convertible to a delegate type solely on the basis of whether the arguments match, not also on the basis of whether the return type matches. Lambdas check both the arguments and the return type.
The reason for this odd rule is that method group conversions to delegates are essentially a solution of the overload resolution problem. Suppose D is the delegate type
double D(string s)
and M is a method group containing a method that takes a string and returns a string. When resolving the meaning of a conversion from M to D, we do overload resolution as if you had said M(string). Overload resolution would pick the M that takes a string and returns a string, and so M is convertible to that delegate type even though the conversion will result in an error later. Just as "regular" overload resolution would succeed if you said "string s = M(null);" -- overload resolution succeeds, even though that causes a conversion failure later.This rule is subtle and a bit weird. The upshot of it here is that your method group is convertible to all the different delegate types that are the second arguments of every version of Sum that takes a delegate. Since no best conversion can be found, the overload resolution on method group
Sum
is ambiguous.Method group conversions rules are plausible but a bit odd in C#. I am somewhat vexed that they are not consistent with the more "intuitively correct" lambda conversions.
<代码> s => GetValue(s) 是一个 lambda 表达式,而(...) 的语法糖,但它们相互关联的唯一方式是 lambda 表达式包含对 GetValue 的调用()。在转换为委托时,方法组在返回类型方面具有与 lambda 表达式不同的转换规则。请参阅 为什么 Func与 Func>? 和 重载方法不明确-group 参数混淆了重载解析?。
GetValue
是一个方法组,这是完全不同的事情。它们都可以被视为 new Funcs => GetValue(s)
is a lambda expression andGetValue
is a method group, which is a completely different thing. They can both be considered syntactic sugar fornew Func<string,double>(...)
but the only way they are related to each other is that the lambda expression includes a call toGetValue()
. When it comes to converting to a delegate, method groups have different conversion rules than lambda expressions with respect to return types. See Why is Func<T> ambiguous with Func<IEnumerable<T>>? and Overloaded method-group argument confuses overload resolution?.请注意,从最新的编译器版本开始,这是有效的(@sharplab、@dotnetfiddle 与 .NET Core 3.1)。对于那些不胆怯的人,您可以深入了解 方法组转换是规范的一部分,以查看到底发生了什么变化导致了这种效果。
Note that as of latest compiler versions this works (@sharplab, @dotnetfiddle with .NET Core 3.1). For those who are not faint of heart you can do a deep dive into the Method group conversions part of the specification to see what exactly has changed that has lead to such effect.