方法推理不适用于方法组

本文关键字:方法 适用于 不适用 推理 | 更新日期: 2023-09-27 18:11:10

考虑

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

方法推理不适用于方法组

Mark的答案是正确的,但可能需要更多的解释。

这个问题实际上是由于方法组的处理方式和lambda的处理方式之间的细微差异造成的。

具体来说,细微的区别在于方法组被认为可以转换为委托类型,这仅仅是基于参数是否匹配,而不是基于return类型匹配Lambdas检查参数和返回类型。

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

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

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

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