【问题标题】:Why can `s => x.Append(s)` can be passed as an Action<string> but `x.Append` can't?为什么 `s => x.Append(s)` 可以作为 Action<string> 传递,而 `x.Append` 不能?
【发布时间】:2014-06-12 18:05:28
【问题描述】:

在尝试将StringBuilderAppend 方法传递给采用Action&lt;string&gt; 的函数时,我注意到一些奇怪的事情。

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

出于测试目的,我只是想将数据写入StringBuilder,所以我尝试这样称呼它:

var output = new StringBuilder();
DoStuff(output.Append);

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

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

不假思索,我把代码改成了这样:

var output = new StringBuilder();
DoStuff(s => output.Append(s));

这编译得很好。

然后我就糊涂了;意识到s =&gt; output.Append(s) 也应该返回StringBuilder,它们不一样吗?

那么,为什么会这样呢?为什么s =&gt; output.Append(s) 可以静默丢弃返回值,而output.Append 不能?

【问题讨论】:

标签: c# .net lambda


【解决方案1】:

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

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

规范(§6.5)说:

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

  • 如果 F 的主体是表达式,并且 D 具有 void 返回类型或 F 是异步且 D 具有返回类型 Task,那么当 F 的每个参数被赋予 D 中相应参数的类型时, F 的主体是一个有效的表达式(wrt §7),可以作为语句表达式(§8.6)。

【讨论】:

  • 我很困惑如何不使用多行 lambda 将其转换为:s =&gt; { return output.Append(s); }。我会假设他们是平等的。
  • @gunr2171:不;推断不返回任何东西/
  • @gunr2171 C# 中的 Lambda 表达式总是根据它们的用法进行推断。 s =&gt; output.Append( s ) 本身没有任何意义。它仅在编译器可以推断类型的地方才有意义。这是因为 lambda 表达式也可以与 Expression&lt;Action&lt;string&gt;&gt; expr = s =&gt; output.Append( s ) 等表达式树一起使用。这推断 lambda 是一种甚至不是委托的类型。
  • 这也是为什么var f = s =&gt; output.Append( s );这样的语句是编译错误的原因。编译器无法推断表达式类型,所以var的使用不正确。
  • @DannyTuppeny:请参阅我的编辑以获取相关规范报价。
猜你喜欢
  • 2021-10-10
  • 1970-01-01
  • 1970-01-01
  • 2011-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多