我该如何强制抛出语句是语句而不是表达式(在lambda表达式中)?

时间:2019-01-15 23:08:56

标签: 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*/ }

当这样调用时

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

甚至是这样(带有lambda语句)

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

M(Func <>)重载由编译器选择,指示此处将throw视为表达式。 我如何才能优雅而明确地迫使编译器选择M(Action)重载?

做到这一点的一种方法是

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

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

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

4 个答案:

答案 0 :(得分:6)

您可以为Action添加一个强制类型转换,尽管它确实有点 LISP'y ,并且带有所有括号:

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

不理想,但是如果您想获得最大的清晰度:

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

答案 1 :(得分:6)

这与lambda是语句lambda还是表达式lambda无关(如您将lambda从表达式lambda更改为语句lambda且行为未改变一样,这最简洁地说明了这一点。)

有多种方法可以使lambda匹配多个可能的重载。这是特定于较新版本的,但是自C#1.0起,其他方法也已应用(并且自引入匿名方法以来,必须存在对匿名方法的特定处理以及由此导致的过载解析歧义)。

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

因此,您需要通过使匿名方法对Func<int>无效来防止两个重载匹配,或者显式强制类型为Action,以便编译器本身不会对其进行歧义处理。

答案 2 :(得分: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());
}

这可能应该记录在代码中,以免引起下一个开发人员的注意,并且如注释中所述,编写适当的自动化测试以验证是否调用了正确的重载。

答案 3 :(得分:4)

要添加到给出的所有合理答案中,这是一个迷人的不合理答案:

development.rb

应该有bake the noodle的任何维护程序员来,他们将不理会它。看看是否可以弄清楚它为什么起作用。