“x => {throw ..}”的Lambda推断为在重载方法中匹配Func ?

我不明白为什么C#最终在以下LINQPad代码中执行不正确的扩展方法:

void Main() { // Actual: Sync Action "Expected: Sync Action".Run(x => { x.Dump(); }); // Actual: Async Task "Expected: Async Task".Run(async x => { await System.Threading.Tasks.Task.Run(() => x.Dump()); }); // Actual: Async Task!! "Expected: Sync Action".Run(x => { throw new Exception("Meh"); }); } static class Extensions { public static void Run(this T instance, Action action) { "Actual: Sync Action".Dump(); action(instance); } public static void Run(this T instance, Func func) { "Actual: Async Task".Dump(); func(instance).Wait(); } } 

为什么编译器认为lambda在这里返回一个Task?

我希望在第三次调用Run()时看到“Actual:Sync Action”,因为lambda中没有任何内容表明这是一个Func返回Task。

这只是一个重载解决问题。 显然,lambda x => { throw new Exception("Meh"); } 可以转换为ActionFunc (以及与此问题无关的许多其他委托类型)。 这只是C#的重载决策规则,在这种情况下更喜欢后者。

这是一个更具代表性的例子:

 void Main() { // Output: Func "Test".WhatsThis(x => { throw new Exception("Meh"); }); } static class Extensions { public static void WhatsThis(this T dummy, Action action) { "Action".Dump(); } public static void WhatsThis(this T dummy, Func func) { "Func".Dump(); } } 

至于为什么会出现这种情况,我不是百分百肯定,但随便看一下这个规格就会向我展示下面可能的解释(强调我的):

7.5.3过载分辨率

[…]

7.5.3.3从表达转换更好

给定从表达式E转换为类型T1的隐式转换C1,以及从表达式E转换为类型T2的隐式转换C2,如果以下至少一个成立,则C1是比C2更好的转换:

[…]

•E是匿名函数,T1是委托类型D1或表达式树类型Expression ,T2是委托类型D2或表达式树类型Expression并且以下之一成立:

[…]

•D1的返回类型为Y,D2返回无效