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

问题描述 投票:7回答:4

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

public static void M(Action doIt) { /*use doIt*/ }
public static void M(Func<int> doIt) { /*use doIt*/ }

在调用时这样

M(() => throw new Exception());

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

M(() => { throw new Exception(); });

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

一种方法是这样做

M(() => { throw new Exception(); return; });

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

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

c# lambda expression action func
4个回答
6
投票

你可以为Action添加一个强制转换,虽然它的所有括号都有点LISP'y:

M((Action)(() => throw new Exception()));

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

Action thrw = () => throw new Exception();
M(thrw);

6
投票

这与lambda是一个语句lambda还是一个表达式lambda无关(你最简洁地将lambda从一个表达式lambda更改为一个语句lambda并且行为没有改变)。

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

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

因此,您需要通过使匿名方法对Func<int>无效来防止匹配重载,或者明确强制该类型为Action,以便编译器本身不消除歧义。


4
投票

一种可能的方法是使用命名参数:

public static void M(Action action) { /* do stuff */ }

public static void M(Func<int> func) { /* do stuff */ }

public static void Main()
{
    M(action: () => throw new Exception());
}

这应该记录在代码中,以免下一个开发人员出现,并且如评论中所述,编写一个适当的自动化测试来验证正确的重载被调用。


4
投票

为了增加给出的所有合理答案,这是一个迷人的不合理的答案:

((Action<Action>)M)(() => throw new Exception());

对于任何维护程序员来说,这应该是bake the noodle,他们会不管它。看看你能否找出它的工作原理。

© www.soinside.com 2019 - 2024. All rights reserved.