如何强制将throw作为语句而不是表达式(在lambda表达式中)?

Ulf*_*edt 7 c# lambda expression action func

从C#7.0开始,throw关键字既可以用作表达式,也可以用作语句,这很好.但是,请考虑这些过载

public static void M(Action doIt) { /*use doIt*/ }
public static void M(Func<int> doIt) { /*use doIt*/ }
Run Code Online (Sandbox Code Playgroud)

在调用时这样

M(() => throw new Exception());
Run Code Online (Sandbox Code Playgroud)

或者甚至喜欢这样(带有语句lambda)

M(() => { throw new Exception(); });
Run Code Online (Sandbox Code Playgroud)

编译器选择M(Func <>)重载,指示throw在这里被视为表达式.如何优雅和明确地强制编译器选择M(Action)重载?

一种方法是这样做

M(() => { throw new Exception(); return; });
Run Code Online (Sandbox Code Playgroud)

但返回语句的原因似乎并不明显,并且存在被下一个开发人员更改的风险,特别是因为Resharper警告无法访问的代码.

(当然我可以更改方法命名以避免重载,但这不是问题.:-)

Dav*_*haw 6

您可以为for添加一个强制转换Action,尽管它的所有括号都有点LISP'y:

M((Action)(() => throw new Exception()));
Run Code Online (Sandbox Code Playgroud)

不理想,但如果你想要最大程度的清晰度:

Action thrw = () => throw new Exception();
M(thrw);
Run Code Online (Sandbox Code Playgroud)


Ser*_*rvy 6

这与lambda是语句lambda还是表达式lambda无关(就像你将lambda从表达式lambda更改为语句lambda并且行为没有改变而最简洁地显示).

有许多方法可以使lambda匹配多个可能的重载.这个特定于较新的版本,但是自C#1.0以来已经应用了其他方法(并且自匿名方法的引入以来,匿名方法的特定处理以及由此产生的重载消除歧义消除已经存在).

确定调用哪个重载的规则在C#规范的第7.5.3.3节中详细说明.具体来说,当参数是匿名方法时,它总是更喜欢重载谁的委托(或表达式)的返回值超过没有返回值的返回值.无论是语句lambda还是表达式lambda,都是如此; 它适用于任何形式的匿名函数.

因此,您需要通过使匿名方法对a无效来防止重载进行匹配Func<int>,或者显式强制类型为,Action因此编译器本身并不消除歧义.

  • @JonathonChase:那是对的.要与`Func <int>`*兼容,每个返回的点都必须返回与int*兼容的东西,并且该条件是空的,因为所有零返回中的零符合要求."有趣"是它的一个词."实施的超级乐趣"不是另一种.我们只想说有很多测试用例要编写. (3认同)