这是一个完整的例子,不涉及
Task
,要删除有关异步的任何提示:
using System;
class Program
{
static void Method(Action action)
{
Console.WriteLine("Action");
}
static void Method(Func<int> func)
{
Console.WriteLine("Func<int>");
}
static void ThrowException()
{
throw new Exception();
}
static void Main()
{
// Resolvse to the Action overload
Method(() => ThrowException());
// Resolves to the Func<int> overload
Method(() => { throw new Exception(); });
}
}
使用节编号来自
ECMA 334 (5th edition)
,我们对第12.6.4节-过载分辨率感兴趣。两个重要步骤是:
-
确定适用的方法(12.6.4.2)
-
确定
最好的
方法(12.6.4.3)
我们将依次查看每个呼叫
呼叫1:
() => ThrowException()
让我们从第一个调用开始,它的参数为
()=>throwException()。
. 为了检查适用性,我们需要将该论点转换为
Action
或
Func<int>
. 我们可以在不涉及过载的情况下进行检查:
// Fine
Action action = () => ThrowException();
// Fails to compile:
// error CS0029: Cannot implicitly convert type 'void' to 'int'
// error CS1662: Cannot convert lambda expression to intended delegate type because
// some of the return types in the block are not implicitly convertible to the
// delegate return type
Func<int> func = () => ThrowException();
在这种情况下,CS1662错误的措辞有点不幸——不是块中有一个返回类型不能隐式转换为委托返回类型,而是
不是
lambda表达式中的返回类型。防止这种情况的具体方法见第11.7.1节。那里的任何允许转换都不起作用。最近的是这个(其中f是lambda表达式,d是
功能<int>
):
如果身体
F
是表达式,并且
f
是非异步的,并且
D
具有非空返回类型
T
,或者
f
是异步的
D
具有返回类型
Task<T>
,则当给f的每个参数指定了
D
,的主体
f
是可隐式转换为
T
.
在这种情况下,表达式
ThrowException
是
不
隐式可转换为
int
因此出现了错误。
所有这些都意味着只有第一种方法适用于
()=>throwException()。
. 当一组适用的函数成员只有一个条目时,我们选择“最佳函数成员”就非常容易了…
呼叫2:
() => { throw new Exception(); }
现在让我们看看第二个电话,
()=>引发新的异常();
作为论据。让我们尝试相同的转换:
// Fine
Action action = () => { throw new Exception(); };
// Fine
Func<int> func = () => { throw new Exception(); };
两种转换都在这里工作。后者之所以起作用是因为
这
11.7.1中的项目符号:
如果身体
f
是语句块,并且
f
是非异步的,并且
D
有一个
非空返回类型
T
,或者
f
是异步的
D
具有返回类型
任务<t>
,
然后当每个参数
f
在中给出了相应参数的类型
D
,的主体
f
是一个有效的语句块(W.R.T_§13.3),具有不可更改的
结束点,其中每个返回语句都指定一个隐式表达式
可转换为
T
.
我知道这听起来很奇怪,但是:
-
无法到达块的终点
-
没有返回语句,因此确实满足“每个返回语句指定[…]”的条件。
换一种说法:你
能够
将该块用作已声明返回的方法的主体
int
。
这意味着
二者都
我们的方法适用于这种情况。
那么哪个更好呢?
现在,我们需要看12.6.4.3节,以确定实际选择哪种方法。
有
太多了
这里的规则,但是决定这里事情的是从lambda表达式到
行动
或
功能<int>
. 这在12.6.4.4中得到了解决(更好地从表达式转换):
给定一个从表达式e转换为类型t1的隐式转换c1,以及一个从表达式e转换为类型t2的隐式转换c2,如果至少有以下一个条件成立,c1比c2更好的转换:
-
…
-
e是匿名函数,T1是委托类型d1或表达式树
类型
Expression<D1>
,t2是委托类型d2或表达式树类型
Expression<D2>
和
下列情况之一:
-
d1是比d2更好的转换目标
-
d1和d2具有相同的参数列表,以下其中一个保持不变:
-
d1有一个返回类型y1,d2有一个返回类型y2,在参数列表(§12.6.3.13)的上下文中,e存在一个推断的返回类型x,从x到y1的转换比从x到y2的转换更好。
-
E是异步的。-跳过,因为它不是]
-
d1为返回类型y,d2为空返回
我用粗体写的部分很重要。当您考虑以下情况时:
-
E是
()=>引发新的异常();
-
T1是
功能<int>
(所以d1是
功能<int>
太)
-
T2是
行动
(所以d2是
行动
太)
…然后,d1和d2都有空的参数列表,但d1有返回类型
int
d2为空返回。
因此转化为
功能<int>
比转换为
行动
…也就是说
Method(Action)
函数成员是否比
Member(Func<int>)
第二个电话。
唷!你不喜欢超负荷的解决方案吗?