方法推理不适用于方法组

问题描述 投票:0回答:3

考虑

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

c# .net-4.0 type-inference method-group
3个回答
18
投票

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

问题确实是由于方法组的处理方式和 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

8
投票
new Func<string,double>(...)

的语法糖,但它们彼此相关的唯一方式是 lambda 表达式包含对

GetValue()
的调用。在转换为委托时,方法组在返回类型方面具有与 lambda 表达式不同的转换规则。请参阅
为什么 Func
与 Func
> 不明确?
重载方法组参数混淆重载解析?
请注意,从最新的编译器版本开始,此功能有效(@sharplab

@dotnetfiddle 与 .NET Core 3.1

0
投票
方法组转换

部分,看看到底是什么变化导致了这种效果。

© www.soinside.com 2019 - 2024. All rights reserved.