为什么`s => x.Append(s)`可以作为Action <string>传递但是`x.Append`不能?

Dan*_*eny 16 .net c# lambda

试图通过一个时,我发现了一些奇怪StringBuilderAppend方法来夺走了一个功能Action<string>.

public void DoStuff(Action<string> handler)
{
    // Do stuff, call handler("data");
}
Run Code Online (Sandbox Code Playgroud)

出于测试目的,我只想将数据写入a StringBuilder,所以我试着像这样调用它:

var output = new StringBuilder();
DoStuff(output.Append);
Run Code Online (Sandbox Code Playgroud)

但是,这会产生编译错误,因为该Append方法与所需的签名不匹配(它返回一个引用StringBuilder,而不是我的方法所需的void):

'System.Text.StringBuilder System.Text.StringBuilder.Append(string)'返回类型错误

不假思索,我把代码更改为:

var output = new StringBuilder();
DoStuff(s => output.Append(s));
Run Code Online (Sandbox Code Playgroud)

这编译好了.

然后我感到困惑; 意识到s => output.Append(s)还应该回归StringBuilder,不是他们一样吗?

那么,为什么这有效呢?为什么可以s => output.Append(s)静默丢弃返回值,但output.Append不能?

SLa*_*aks 20

s => output.Append(s)创建一个新的lambda表达式,该表达式(从上下文中)推断出返回类型为void.
因此,忽略表达式主体的值.
这被编译为一个单独的方法,调用Append()并返回void(这与委托完全匹配)

相反,当您尝试将方法组转换为委托时,转换必须完全匹配.

规范(§6.5)说:

具体来说,匿名函数F与提供的委托类型D兼容:

  • 如果F的主体是一个表达式,并且D具有void返回类型或者F是异步而D具有返回类型Task,那么当F的每个参数都给出D中相应参数的类型时,F的主体是一个有效的表达式(wrt§7),它将被允许作为statement-expression(§8.6).

  • 我很困惑如何使用多行lambda将其转换为:`s => {return output.Append(s); }`.我认为他们是平等的. (3认同)
  • 这也是为什么语句如`var f = s => output.Append(s);`是编译错误的原因.编译器无法推断表达式类型,因此`var`的使用不正确. (3认同)
  • @ gunr2171:不; 推断不返回任何东西/ (2认同)
  • @ gunr2171 C#中的Lambda表达式总是根据其用法推断出来.`s => output.Append(s)`本身没有任何意义.它只在编译器可以推断出类型的地方有意义.这是因为lambda表达式也可以用于表达式树,例如`Expression <Action <string >> expr = s => output.Append(s)`.这推断lambda是一个甚至不是委托的类型. (2认同)