我尝试将StringBuilder
的{{1}}方法传递给一个Append
的函数时发现了一些奇怪的事情。
Action<string>
出于测试目的,我只想将数据写入public void DoStuff(Action<string> handler)
{
// Do stuff, call handler("data");
}
,所以我试着这样称呼它:
StringBuilder
但是,这会产生编译错误,因为var output = new StringBuilder();
DoStuff(output.Append);
方法与所需的签名不匹配(它返回一个引用回Append
,而不是我的方法想要的void):
'System.Text.StringBuilder System.Text.StringBuilder.Append(string)'的返回类型错误
我不假思索地将代码更改为:
StringBuilder
此编译罚款。
然后我感到困惑;意识到var output = new StringBuilder();
DoStuff(s => output.Append(s));
也应该返回s => output.Append(s)
,它们不一样吗?
那么,为什么这有效呢?为什么StringBuilder
可以静默丢弃返回值,但s => output.Append(s)
不能?
答案 0 :(得分:20)
s => output.Append(s)
创建一个新的lambda表达式,该表达式(从上下文中推断)返回类型为void
。
因此,忽略表达式主体的值
这被编译为一个单独的方法,该方法调用Append()
并返回void(这与委托完全匹配)
相反,当您尝试将方法组转换为委托时,转换必须完全匹配。
规范(§6.5)说:
具体来说,匿名函数F与提供的代理类型D兼容:
- 如果F的主体是表达式,并且D具有void返回类型或者F是异步而D具有返回类型Task,那么当F的每个参数被赋予D中相应参数的类型时, F的主体是一个有效的表达式(wrt§7),它将被允许作为语句表达式(§8.6)。